From 684025175ce2155b158e0bf0f72e355d1f4d8b4b Mon Sep 17 00:00:00 2001 From: tsteven4 <13596209+tsteven4@users.noreply.github.com> Date: Mon, 25 Oct 2021 05:26:34 -0600 Subject: [PATCH] update included libusb for mac to 1.0.24 (#744) --- CMakeLists.txt | 4 +- libusb.pri | 4 +- mac/libusb/README | 2 +- mac/libusb/Xcode/config.h | 38 +- mac/libusb/core.c | 580 ++++++++++++-------- mac/libusb/descriptor.c | 793 +++++++++++++-------------- mac/libusb/hotplug.c | 88 +-- mac/libusb/hotplug.h | 18 +- mac/libusb/io.c | 973 ++++++++++++++++----------------- mac/libusb/libusb.h | 530 ++++++++++-------- mac/libusb/libusb.pro | 4 +- mac/libusb/libusbi.h | 809 ++++++++++++++++----------- mac/libusb/os/darwin_usb.c | 990 ++++++++++++++++++++-------------- mac/libusb/os/darwin_usb.h | 12 +- mac/libusb/os/events_posix.c | 300 +++++++++++ mac/libusb/os/events_posix.h | 59 ++ mac/libusb/os/poll_posix.c | 84 --- mac/libusb/os/poll_posix.h | 11 - mac/libusb/os/threads_posix.c | 113 ++-- mac/libusb/os/threads_posix.h | 42 +- mac/libusb/strerror.c | 81 +-- mac/libusb/sync.c | 45 +- mac/libusb/version.h | 2 +- mac/libusb/version_nano.h | 2 +- 24 files changed, 3262 insertions(+), 2322 deletions(-) create mode 100644 mac/libusb/os/events_posix.c create mode 100644 mac/libusb/os/events_posix.h delete mode 100644 mac/libusb/os/poll_posix.c delete mode 100644 mac/libusb/os/poll_posix.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ccb7b6a8..65358f702 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -241,7 +241,7 @@ if(APPLE) mac/libusb/strerror.c mac/libusb/sync.c mac/libusb/os/darwin_usb.c - mac/libusb/os/poll_posix.c + mac/libusb/os/events_posix.c mac/libusb/os/threads_posix.c ) set(HEADERS ${HEADERS} @@ -251,7 +251,7 @@ if(APPLE) mac/libusb/version.h mac/libusb/version_nano.h mac/libusb/os/darwin_usb.h - mac/libusb/os/poll_posix.h + mac/libusb/os/events_posix.h mac/libusb/os/threads_posix.h ) add_compile_options(-Wall -Wsign-compare) diff --git a/libusb.pri b/libusb.pri index 477929185..7569f113a 100644 --- a/libusb.pri +++ b/libusb.pri @@ -42,7 +42,7 @@ macx|linux|openbsd { mac/libusb/strerror.c \ mac/libusb/sync.c \ mac/libusb/os/darwin_usb.c \ - mac/libusb/os/poll_posix.c \ + mac/libusb/os/events_posix.c \ mac/libusb/os/threads_posix.c HEADERS += mac/libusb/hotplug.h \ mac/libusb/libusb.h \ @@ -50,7 +50,7 @@ macx|linux|openbsd { mac/libusb/version.h \ mac/libusb/version_nano.h \ mac/libusb/os/darwin_usb.h \ - mac/libusb/os/poll_posix.h \ + mac/libusb/os/events_posix.h \ mac/libusb/os/threads_posix.h } else:equals(WITH_LIBUSB, custom) { message("libusb-1.0 is enabled but but must be manually configured") diff --git a/mac/libusb/README b/mac/libusb/README index 498ebd813..77ef9b8a0 100644 --- a/mac/libusb/README +++ b/mac/libusb/README @@ -1,4 +1,4 @@ -This is libusb-1.0.22 from https://libusb.info/. +This is libusb-1.0.24 from https://libusb.info/. Since we have such problems with people getting libusb successfully built - between the Universal Build issues and the fact that we have to work hard to go find where it's installed diff --git a/mac/libusb/Xcode/config.h b/mac/libusb/Xcode/config.h index 14d152729..59f346335 100644 --- a/mac/libusb/Xcode/config.h +++ b/mac/libusb/Xcode/config.h @@ -1,25 +1,37 @@ /* config.h. Manually generated for Xcode. */ -/* Default visibility */ -#define DEFAULT_VISIBILITY /**/ +#include -/* Message logging */ +/* Define to the attribute for default visibility. */ +#define DEFAULT_VISIBILITY __attribute__ ((visibility ("default"))) + +/* Define to 1 to enable message logging. */ #define ENABLE_LOGGING 1 -/* Define to 1 if you have the header file. */ -#define HAVE_POLL_H 1 +/* On 10.12 and later, use newly available clock_*() functions */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 +/* Define to 1 if you have the `clock_gettime' function. */ +#define HAVE_CLOCK_GETTIME 1 +#endif + +/* On 10.6 and later, use newly available pthread_threadid_np() function */ +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 +/* Define to 1 if you have the 'pthread_threadid_np' function. */ +#define HAVE_PTHREAD_THREADID_NP 1 +#endif + +/* Define to 1 if the system has the type `nfds_t'. */ +#define HAVE_NFDS_T 1 /* Define to 1 if you have the header file. */ #define HAVE_SYS_TIME_H 1 -/* Darwin backend */ -#define OS_DARWIN 1 - -/* type of second poll() argument */ -#define POLL_NFDS_TYPE nfds_t +/* Define to 1 if compiling for a POSIX platform. */ +#define PLATFORM_POSIX 1 -/* Use POSIX Threads */ -#define THREADS_POSIX 1 +/* Define to the attribute for enabling parameter checks on printf-like + functions. */ +#define PRINTF_FORMAT(a, b) __attribute__ ((__format__ (__printf__, a, b))) -/* Use GNU extensions */ +/* Enable GNU extensions. */ #define _GNU_SOURCE 1 diff --git a/mac/libusb/core.c b/mac/libusb/core.c index 50f92f6b1..07d459c12 100644 --- a/mac/libusb/core.c +++ b/mac/libusb/core.c @@ -20,37 +20,29 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config.h" +#include "libusbi.h" +#include "hotplug.h" +#include "version.h" -#include -#include +#ifdef __ANDROID__ +#include +#endif #include -#include #include -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef HAVE_SYSLOG_H +#ifdef HAVE_SYSLOG #include #endif -#ifdef __ANDROID__ -#include -#endif - -#include "libusbi.h" -#include "hotplug.h" - -struct libusb_context *usbi_default_context = NULL; +struct libusb_context *usbi_default_context; static const struct libusb_version libusb_version_internal = { LIBUSB_MAJOR, LIBUSB_MINOR, LIBUSB_MICRO, LIBUSB_NANO, LIBUSB_RC, "http://libusb.info" }; -static int default_context_refcnt = 0; +static int default_context_refcnt; static usbi_mutex_static_t default_context_lock = USBI_MUTEX_INITIALIZER; -static struct timespec timestamp_origin = { 0, 0 }; +static struct timespec timestamp_origin; +#if defined(ENABLE_LOGGING) && !defined(USE_SYSTEM_LOGGING_FACILITY) +static libusb_log_cb log_handler; +#endif usbi_mutex_static_t active_contexts_lock = USBI_MUTEX_INITIALIZER; struct list_head active_contexts_list; @@ -61,7 +53,7 @@ struct list_head active_contexts_list; * \section intro Introduction * * libusb is an open source library that allows you to communicate with USB - * devices from userspace. For more info, see the + * devices from user space. For more info, see the * libusb homepage. * * This documentation is aimed at application developers wishing to @@ -165,6 +157,36 @@ struct list_head active_contexts_list; /** * \page libusb_caveats Caveats * + * \section threadsafety Thread safety + * + * libusb is designed to be completely thread-safe, but as with any API it + * cannot prevent a user from sabotaging themselves, either intentionally or + * otherwise. + * + * Observe the following general guidelines: + * + * - Calls to functions that release a resource (e.g. libusb_close(), + * libusb_free_config_descriptor()) should not be called concurrently on + * the same resource. This is no different than concurrently calling free() + * on the same allocated pointer. + * - Each individual \ref libusb_transfer should be prepared by a single + * thread. In other words, no two threads should ever be concurrently + * filling out the fields of a \ref libusb_transfer. You can liken this to + * calling sprintf() with the same destination buffer from multiple threads. + * The results will likely not be what you want unless the input parameters + * are all the same, but its best to avoid this situation entirely. + * - Both the \ref libusb_transfer structure and its associated data buffer + * should not be accessed between the time the transfer is submitted and the + * time the completion callback is invoked. You can think of "ownership" of + * these things as being transferred to libusb while the transfer is active. + * - The various "setter" functions (e.g. libusb_set_log_cb(), + * libusb_set_pollfd_notifiers()) should not be called concurrently on the + * resource. Though doing so will not lead to any undefined behavior, it + * will likely produce results that the application does not expect. + * + * Rules for multiple threads and asynchronous I/O are detailed + * \ref libusb_mtasync "here". + * * \section fork Fork considerations * * libusb is not designed to work across fork() calls. Depending on @@ -191,12 +213,12 @@ struct list_head active_contexts_list; * you when this has happened, so if someone else resets your device it will * not be clear to your own program why the device state has changed. * - * Ultimately, this is a limitation of writing drivers in userspace. + * Ultimately, this is a limitation of writing drivers in user space. * Separation from the USB stack in the underlying kernel makes it difficult * for the operating system to deliver such notifications to your program. * The Linux kernel USB stack allows such reset notifications to be delivered * to in-kernel USB drivers, but it is not clear how such notifications could - * be delivered to second-class drivers that live in userspace. + * be delivered to second-class drivers that live in user space. * * \section blockonly Blocking-only functionality * @@ -424,6 +446,7 @@ if (cfg != desired) * - libusb_set_auto_detach_kernel_driver() * - libusb_set_configuration() * - libusb_set_debug() + * - libusb_set_log_cb() * - libusb_set_interface_alt_setting() * - libusb_set_iso_packet_lengths() * - libusb_set_option() @@ -438,6 +461,7 @@ if (cfg != desired) * - libusb_unlock_event_waiters() * - libusb_unref_device() * - libusb_wait_for_event() + * - libusb_wrap_sys_device() * * \section Structures * - libusb_bos_descriptor @@ -466,6 +490,7 @@ if (cfg != desired) * - \ref libusb_class_code * - \ref libusb_descriptor_type * - \ref libusb_endpoint_direction + * - \ref libusb_endpoint_transfer_type * - \ref libusb_error * - \ref libusb_iso_sync_type * - \ref libusb_iso_usage_type @@ -674,17 +699,12 @@ struct libusb_device *usbi_alloc_device(struct libusb_context *ctx, unsigned long session_id) { size_t priv_size = usbi_backend.device_priv_size; - struct libusb_device *dev = calloc(1, sizeof(*dev) + priv_size); - int r; + struct libusb_device *dev = calloc(1, PTR_ALIGN(sizeof(*dev)) + priv_size); if (!dev) return NULL; - r = usbi_mutex_init(&dev->lock); - if (r) { - free(dev); - return NULL; - } + usbi_mutex_init(&dev->lock); dev->ctx = ctx; dev->refcnt = 1; @@ -742,21 +762,22 @@ void usbi_disconnect_device(struct libusb_device *dev) * to the discovered device list. */ int usbi_sanitize_device(struct libusb_device *dev) { - int r; uint8_t num_configurations; - r = usbi_device_cache_descriptor(dev); - if (r < 0) - return r; + if (dev->device_descriptor.bLength != LIBUSB_DT_DEVICE_SIZE || + dev->device_descriptor.bDescriptorType != LIBUSB_DT_DEVICE) { + usbi_err(DEVICE_CTX(dev), "invalid device descriptor"); + return LIBUSB_ERROR_IO; + } num_configurations = dev->device_descriptor.bNumConfigurations; if (num_configurations > USB_MAXCONFIG) { usbi_err(DEVICE_CTX(dev), "too many configurations"); return LIBUSB_ERROR_IO; - } else if (0 == num_configurations) + } else if (0 == num_configurations) { usbi_dbg("zero configurations, maybe an unauthorized device"); + } - dev->num_configurations = num_configurations; return 0; } @@ -770,11 +791,12 @@ struct libusb_device *usbi_get_device_by_session_id(struct libusb_context *ctx, struct libusb_device *ret = NULL; usbi_mutex_lock(&ctx->usb_devs_lock); - list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) + for_each_device(ctx, dev) { if (dev->session_data == session_id) { ret = libusb_ref_device(dev); break; } + } usbi_mutex_unlock(&ctx->usb_devs_lock); return ret; @@ -807,12 +829,14 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, struct libusb_device **ret; int r = 0; ssize_t i, len; - USBI_GET_CONTEXT(ctx); - usbi_dbg(""); + + usbi_dbg(" "); if (!discdevs) return LIBUSB_ERROR_NO_MEM; + ctx = usbi_get_context(ctx); + if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { /* backend provides hotplug support */ struct libusb_device *dev; @@ -821,7 +845,7 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, usbi_backend.hotplug_poll(); usbi_mutex_lock(&ctx->usb_devs_lock); - list_for_each_entry(dev, &ctx->usb_devs, list, struct libusb_device) { + for_each_device(ctx, dev) { discdevs = discovered_devs_append(discdevs, dev); if (!discdevs) { @@ -841,8 +865,8 @@ ssize_t API_EXPORTED libusb_get_device_list(libusb_context *ctx, } /* convert discovered_devs into a list */ - len = discdevs->len; - ret = calloc(len + 1, sizeof(struct libusb_device *)); + len = (ssize_t)discdevs->len; + ret = calloc((size_t)len + 1, sizeof(struct libusb_device *)); if (!ret) { len = LIBUSB_ERROR_NO_MEM; goto out; @@ -925,7 +949,7 @@ uint8_t API_EXPORTED libusb_get_port_number(libusb_device *dev) * \returns LIBUSB_ERROR_OVERFLOW if the array is too small */ int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, - uint8_t* port_numbers, int port_numbers_len) + uint8_t *port_numbers, int port_numbers_len) { int i = port_numbers_len; struct libusb_context *ctx = DEVICE_CTX(dev); @@ -948,10 +972,10 @@ int API_EXPORTED libusb_get_port_numbers(libusb_device *dev, } /** \ingroup libusb_dev - * Deprecated please use libusb_get_port_numbers instead. + * \deprecated Please use \ref libusb_get_port_numbers() instead. */ int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev, - uint8_t* port_numbers, uint8_t port_numbers_len) + uint8_t *port_numbers, uint8_t port_numbers_len) { UNUSED(ctx); @@ -966,7 +990,7 @@ int API_EXPORTED libusb_get_port_path(libusb_context *ctx, libusb_device *dev, * function and make sure that you only access the parent before issuing * \ref libusb_free_device_list(). The reason is that libusb currently does * not maintain a permanent list of device instances, and therefore can - * only guarantee that parents are fully instantiated within a + * only guarantee that parents are fully instantiated within a * libusb_get_device_list() - libusb_free_device_list() block. */ DEFAULT_VISIBILITY @@ -1075,7 +1099,9 @@ out: * If acting on an isochronous or interrupt endpoint, this function will * multiply the value found in bits 0:10 by the number of transactions per * microframe (determined by bits 11:12). Otherwise, this function just - * returns the numeric value found in bits 0:10. + * returns the numeric value found in bits 0:10. For USB 3.0 device, it + * will attempts to retrieve the Endpoint Companion Descriptor to return + * wBytesPerInterval. * * This function is useful for setting up isochronous transfers, for example * you might pass the return value from this function to @@ -1095,9 +1121,11 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, { struct libusb_config_descriptor *config; const struct libusb_endpoint_descriptor *ep; - enum libusb_transfer_type ep_type; + struct libusb_ss_endpoint_companion_descriptor *ss_ep_cmp; + enum libusb_endpoint_transfer_type ep_type; uint16_t val; int r; + int speed; r = libusb_get_active_config_descriptor(dev, &config); if (r < 0) { @@ -1112,13 +1140,25 @@ int API_EXPORTED libusb_get_max_iso_packet_size(libusb_device *dev, goto out; } - val = ep->wMaxPacketSize; - ep_type = (enum libusb_transfer_type) (ep->bmAttributes & 0x3); + speed = libusb_get_device_speed(dev); + if (speed >= LIBUSB_SPEED_SUPER) { + r = libusb_get_ss_endpoint_companion_descriptor(dev->ctx, ep, &ss_ep_cmp); + if (r == LIBUSB_SUCCESS) { + r = ss_ep_cmp->wBytesPerInterval; + libusb_free_ss_endpoint_companion_descriptor(ss_ep_cmp); + } + } - r = val & 0x07ff; - if (ep_type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS - || ep_type == LIBUSB_TRANSFER_TYPE_INTERRUPT) - r *= (1 + ((val >> 11) & 3)); + /* If the device isn't a SuperSpeed device or retrieving the SS endpoint didn't worked. */ + if (speed < LIBUSB_SPEED_SUPER || r < 0) { + val = ep->wMaxPacketSize; + ep_type = (enum libusb_endpoint_transfer_type) (ep->bmAttributes & 0x3); + + r = val & 0x07ff; + if (ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS + || ep_type == LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT) + r *= (1 + ((val >> 11) & 3)); + } out: libusb_free_config_descriptor(config); @@ -1173,41 +1213,70 @@ void API_EXPORTED libusb_unref_device(libusb_device *dev) } } -/* - * Signal the event pipe so that the event handling thread will be - * interrupted to process an internal event. +/** \ingroup libusb_dev + * Wrap a platform-specific system device handle and obtain a libusb device + * handle for the underlying device. The handle allows you to use libusb to + * perform I/O on the device in question. + * + * Must call libusb_set_option(NULL, LIBUSB_OPTION_WEAK_AUTHORITY) + * before libusb_init if don't have authority to access the usb device directly. + * + * On Linux, the system device handle must be a valid file descriptor opened + * on the device node. + * + * The system device handle must remain open until libusb_close() is called. + * The system device handle will not be closed by libusb_close(). + * + * Internally, this function creates a temporary device and makes it + * available to you through libusb_get_device(). This device is destroyed + * during libusb_close(). The device shall not be opened through libusb_open(). + * + * This is a non-blocking function; no requests are sent over the bus. + * + * \param ctx the context to operate on, or NULL for the default context + * \param sys_dev the platform-specific system device handle + * \param dev_handle output location for the returned device handle pointer. Only + * populated when the return code is 0. + * \returns 0 on success + * \returns LIBUSB_ERROR_NO_MEM on memory allocation failure + * \returns LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * \returns LIBUSB_ERROR_NOT_SUPPORTED if the operation is not supported on this + * platform + * \returns another LIBUSB_ERROR code on other failure */ -int usbi_signal_event(struct libusb_context *ctx) +int API_EXPORTED libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, + libusb_device_handle **dev_handle) { - unsigned char dummy = 1; - ssize_t r; + struct libusb_device_handle *_dev_handle; + size_t priv_size = usbi_backend.device_handle_priv_size; + int r; - /* write some data on event pipe to interrupt event handlers */ - r = usbi_write(ctx->event_pipe[1], &dummy, sizeof(dummy)); - if (r != sizeof(dummy)) { - usbi_warn(ctx, "internal signalling write failed"); - return LIBUSB_ERROR_IO; - } + usbi_dbg("wrap_sys_device 0x%" PRIxPTR, (uintptr_t)sys_dev); - return 0; -} + ctx = usbi_get_context(ctx); -/* - * Clear the event pipe so that the event handling will no longer be - * interrupted. - */ -int usbi_clear_event(struct libusb_context *ctx) -{ - unsigned char dummy; - ssize_t r; + if (!usbi_backend.wrap_sys_device) + return LIBUSB_ERROR_NOT_SUPPORTED; - /* read some data on event pipe to clear it */ - r = usbi_read(ctx->event_pipe[0], &dummy, sizeof(dummy)); - if (r != sizeof(dummy)) { - usbi_warn(ctx, "internal signalling read failed"); - return LIBUSB_ERROR_IO; + _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size); + if (!_dev_handle) + return LIBUSB_ERROR_NO_MEM; + + usbi_mutex_init(&_dev_handle->lock); + + r = usbi_backend.wrap_sys_device(ctx, _dev_handle, sys_dev); + if (r < 0) { + usbi_dbg("wrap_sys_device 0x%" PRIxPTR " returns %d", (uintptr_t)sys_dev, r); + usbi_mutex_destroy(&_dev_handle->lock); + free(_dev_handle); + return r; } + usbi_mutex_lock(&ctx->open_devs_lock); + list_add(&_dev_handle->list, &ctx->open_devs); + usbi_mutex_unlock(&ctx->open_devs_lock); + *dev_handle = _dev_handle; + return 0; } @@ -1243,20 +1312,13 @@ int API_EXPORTED libusb_open(libusb_device *dev, return LIBUSB_ERROR_NO_DEVICE; } - _dev_handle = malloc(sizeof(*_dev_handle) + priv_size); + _dev_handle = calloc(1, PTR_ALIGN(sizeof(*_dev_handle)) + priv_size); if (!_dev_handle) return LIBUSB_ERROR_NO_MEM; - r = usbi_mutex_init(&_dev_handle->lock); - if (r) { - free(_dev_handle); - return LIBUSB_ERROR_OTHER; - } + usbi_mutex_init(&_dev_handle->lock); _dev_handle->dev = libusb_ref_device(dev); - _dev_handle->auto_detach_kernel_driver = 0; - _dev_handle->claimed_interfaces = 0; - memset(&_dev_handle->os_priv, 0, priv_size); r = usbi_backend.open(_dev_handle); if (r < 0) { @@ -1337,7 +1399,7 @@ static void do_close(struct libusb_context *ctx, usbi_mutex_lock(&ctx->flying_transfers_lock); /* safe iteration because transfers may be being deleted */ - list_for_each_entry_safe(itransfer, tmp, &ctx->flying_transfers, list, struct usbi_transfer) { + for_each_transfer_safe(ctx, itransfer, tmp) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); @@ -1395,12 +1457,12 @@ static void do_close(struct libusb_context *ctx, void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) { struct libusb_context *ctx; + unsigned int event_flags; int handling_events; - int pending_events; if (!dev_handle) return; - usbi_dbg(""); + usbi_dbg(" "); ctx = HANDLE_CTX(dev_handle); handling_events = usbi_handling_events(ctx); @@ -1417,10 +1479,11 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) /* Record that we are closing a device. * Only signal an event if there are no prior pending events. */ usbi_mutex_lock(&ctx->event_data_lock); - pending_events = usbi_pending_events(ctx); - ctx->device_close++; - if (!pending_events) - usbi_signal_event(ctx); + event_flags = ctx->event_flags; + if (!ctx->device_close++) + ctx->event_flags |= USBI_EVENT_DEVICE_CLOSE; + if (!event_flags) + usbi_signal_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); /* take event handling lock */ @@ -1434,10 +1497,10 @@ void API_EXPORTED libusb_close(libusb_device_handle *dev_handle) /* We're done with closing this device. * Clear the event pipe if there are no further pending events. */ usbi_mutex_lock(&ctx->event_data_lock); - ctx->device_close--; - pending_events = usbi_pending_events(ctx); - if (!pending_events) - usbi_clear_event(ctx); + if (!--ctx->device_close) + ctx->event_flags &= ~USBI_EVENT_DEVICE_CLOSE; + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); /* Release event handling lock and wake up event waiters */ @@ -1482,29 +1545,30 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, int *config) { int r = LIBUSB_ERROR_NOT_SUPPORTED; + uint8_t tmp = 0; - usbi_dbg(""); + usbi_dbg(" "); if (usbi_backend.get_configuration) - r = usbi_backend.get_configuration(dev_handle, config); + r = usbi_backend.get_configuration(dev_handle, &tmp); if (r == LIBUSB_ERROR_NOT_SUPPORTED) { - uint8_t tmp = 0; usbi_dbg("falling back to control message"); r = libusb_control_transfer(dev_handle, LIBUSB_ENDPOINT_IN, LIBUSB_REQUEST_GET_CONFIGURATION, 0, 0, &tmp, 1, 1000); - if (r == 0) { + if (r == 1) { + r = 0; + } else if (r == 0) { usbi_err(HANDLE_CTX(dev_handle), "zero bytes returned in ctrl transfer?"); r = LIBUSB_ERROR_IO; - } else if (r == 1) { - r = 0; - *config = tmp; } else { usbi_dbg("control failed, error %d", r); } } - if (r == 0) - usbi_dbg("active config %d", *config); + if (r == 0) { + usbi_dbg("active config %u", tmp); + *config = (int)tmp; + } return r; } @@ -1523,6 +1587,11 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * causing most USB-related device state to be reset (altsetting reset to zero, * endpoint halts cleared, toggles reset). * + * Not all backends support setting the configuration from user space, which + * will be indicated by the return code LIBUSB_ERROR_NOT_SUPPORTED. As this + * suggests that the platform is handling the device configuration itself, + * this error should generally be safe to ignore. + * * You cannot change/reset configuration if your application has claimed * interfaces. It is advised to set the desired configuration before claiming * interfaces. @@ -1552,6 +1621,8 @@ int API_EXPORTED libusb_get_configuration(libusb_device_handle *dev_handle, * \returns 0 on success * \returns LIBUSB_ERROR_NOT_FOUND if the requested configuration does not exist * \returns LIBUSB_ERROR_BUSY if interfaces are currently claimed + * \returns LIBUSB_ERROR_NOT_SUPPORTED if setting or changing the configuration + * is not supported by the backend * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns another LIBUSB_ERROR code on other failure * \see libusb_set_auto_detach_kernel_driver() @@ -1560,6 +1631,8 @@ int API_EXPORTED libusb_set_configuration(libusb_device_handle *dev_handle, int configuration) { usbi_dbg("configuration %d", configuration); + if (configuration < -1 || configuration > (int)UINT8_MAX) + return LIBUSB_ERROR_INVALID_PARAM; return usbi_backend.set_configuration(dev_handle, configuration); } @@ -1597,19 +1670,19 @@ int API_EXPORTED libusb_claim_interface(libusb_device_handle *dev_handle, int r = 0; usbi_dbg("interface %d", interface_number); - if (interface_number >= USB_MAXINTERFACES) + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; usbi_mutex_lock(&dev_handle->lock); - if (dev_handle->claimed_interfaces & (1 << interface_number)) + if (dev_handle->claimed_interfaces & (1U << interface_number)) goto out; - r = usbi_backend.claim_interface(dev_handle, interface_number); + r = usbi_backend.claim_interface(dev_handle, (uint8_t)interface_number); if (r == 0) - dev_handle->claimed_interfaces |= 1 << interface_number; + dev_handle->claimed_interfaces |= 1U << interface_number; out: usbi_mutex_unlock(&dev_handle->lock); @@ -1641,18 +1714,18 @@ int API_EXPORTED libusb_release_interface(libusb_device_handle *dev_handle, int r; usbi_dbg("interface %d", interface_number); - if (interface_number >= USB_MAXINTERFACES) + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev_handle->lock); - if (!(dev_handle->claimed_interfaces & (1 << interface_number))) { + if (!(dev_handle->claimed_interfaces & (1U << interface_number))) { r = LIBUSB_ERROR_NOT_FOUND; goto out; } - r = usbi_backend.release_interface(dev_handle, interface_number); + r = usbi_backend.release_interface(dev_handle, (uint8_t)interface_number); if (r == 0) - dev_handle->claimed_interfaces &= ~(1 << interface_number); + dev_handle->claimed_interfaces &= ~(1U << interface_number); out: usbi_mutex_unlock(&dev_handle->lock); @@ -1685,7 +1758,9 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand { usbi_dbg("interface %d altsetting %d", interface_number, alternate_setting); - if (interface_number >= USB_MAXINTERFACES) + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + if (alternate_setting < 0 || alternate_setting > (int)UINT8_MAX) return LIBUSB_ERROR_INVALID_PARAM; usbi_mutex_lock(&dev_handle->lock); @@ -1694,14 +1769,14 @@ int API_EXPORTED libusb_set_interface_alt_setting(libusb_device_handle *dev_hand return LIBUSB_ERROR_NO_DEVICE; } - if (!(dev_handle->claimed_interfaces & (1 << interface_number))) { + if (!(dev_handle->claimed_interfaces & (1U << interface_number))) { usbi_mutex_unlock(&dev_handle->lock); return LIBUSB_ERROR_NOT_FOUND; } usbi_mutex_unlock(&dev_handle->lock); - return usbi_backend.set_interface_altsetting(dev_handle, interface_number, - alternate_setting); + return usbi_backend.set_interface_altsetting(dev_handle, + (uint8_t)interface_number, (uint8_t)alternate_setting); } /** \ingroup libusb_dev @@ -1751,11 +1826,14 @@ int API_EXPORTED libusb_clear_halt(libusb_device_handle *dev_handle, */ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) { - usbi_dbg(""); + usbi_dbg(" "); if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; - return usbi_backend.reset_device(dev_handle); + if (usbi_backend.reset_device) + return usbi_backend.reset_device(dev_handle); + else + return LIBUSB_ERROR_NOT_SUPPORTED; } /** \ingroup libusb_asyncio @@ -1782,7 +1860,10 @@ int API_EXPORTED libusb_reset_device(libusb_device_handle *dev_handle) int API_EXPORTED libusb_alloc_streams(libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, int num_endpoints) { - usbi_dbg("streams %u eps %d", (unsigned) num_streams, num_endpoints); + usbi_dbg("streams %u eps %d", (unsigned)num_streams, num_endpoints); + + if (!num_streams || !endpoints || num_endpoints <= 0) + return LIBUSB_ERROR_INVALID_PARAM; if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; @@ -1811,6 +1892,9 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, { usbi_dbg("eps %d", num_endpoints); + if (!endpoints || num_endpoints <= 0) + return LIBUSB_ERROR_INVALID_PARAM; + if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; @@ -1833,7 +1917,7 @@ int API_EXPORTED libusb_free_streams(libusb_device_handle *dev_handle, * the same cache lines) when a transfer is in progress, although it is legal * to have several transfers going on within the same memory block. * - * Will return NULL on failure. Many systems do not support such zerocopy + * Will return NULL on failure. Many systems do not support such zero-copy * and will always return NULL. Memory allocated with this function must be * freed with \ref libusb_dev_mem_free. Specifically, this means that the * flag \ref LIBUSB_TRANSFER_FREE_BUFFER cannot be used to free memory allocated @@ -1897,11 +1981,14 @@ int API_EXPORTED libusb_kernel_driver_active(libusb_device_handle *dev_handle, { usbi_dbg("interface %d", interface_number); + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.kernel_driver_active) - return usbi_backend.kernel_driver_active(dev_handle, interface_number); + return usbi_backend.kernel_driver_active(dev_handle, (uint8_t)interface_number); else return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -1932,11 +2019,14 @@ int API_EXPORTED libusb_detach_kernel_driver(libusb_device_handle *dev_handle, { usbi_dbg("interface %d", interface_number); + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.detach_kernel_driver) - return usbi_backend.detach_kernel_driver(dev_handle, interface_number); + return usbi_backend.detach_kernel_driver(dev_handle, (uint8_t)interface_number); else return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -1966,11 +2056,14 @@ int API_EXPORTED libusb_attach_kernel_driver(libusb_device_handle *dev_handle, { usbi_dbg("interface %d", interface_number); + if (interface_number < 0 || interface_number >= USB_MAXINTERFACES) + return LIBUSB_ERROR_INVALID_PARAM; + if (!dev_handle->dev->attached) return LIBUSB_ERROR_NO_DEVICE; if (usbi_backend.attach_kernel_driver) - return usbi_backend.attach_kernel_driver(dev_handle, interface_number); + return usbi_backend.attach_kernel_driver(dev_handle, (uint8_t)interface_number); else return LIBUSB_ERROR_NOT_SUPPORTED; } @@ -2014,7 +2107,7 @@ int API_EXPORTED libusb_set_auto_detach_kernel_driver( void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) { #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); if (!ctx->debug_fixed) { level = CLAMP(level, LIBUSB_LOG_LEVEL_NONE, LIBUSB_LOG_LEVEL_DEBUG); ctx->debug = (enum libusb_log_level)level; @@ -2025,6 +2118,50 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) #endif } +/** \ingroup libusb_lib + * Set log handler. + * + * libusb will redirect its log messages to the provided callback function. + * libusb supports redirection of per context and global log messages. + * Log messages sent to the context will be sent to the global log handler too. + * + * If libusb is compiled without message logging or USE_SYSTEM_LOGGING_FACILITY + * is defined then global callback function will never be called. + * If ENABLE_DEBUG_LOGGING is defined then per context callback function will + * never be called. + * + * \param ctx context on which to assign log handler, or NULL for the default + * context. Parameter ignored if only LIBUSB_LOG_CB_GLOBAL mode is requested. + * \param cb pointer to the callback function, or NULL to stop log + * messages redirection + * \param mode mode of callback function operation. Several modes can be + * selected for a single callback function, see \ref libusb_log_cb_mode for + * a description. + * \see libusb_log_cb, libusb_log_cb_mode + */ +void API_EXPORTED libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, + int mode) +{ +#if defined(ENABLE_LOGGING) && (!defined(ENABLE_DEBUG_LOGGING) || !defined(USE_SYSTEM_LOGGING_FACILITY)) +#if !defined(USE_SYSTEM_LOGGING_FACILITY) + if (mode & LIBUSB_LOG_CB_GLOBAL) + log_handler = cb; +#endif +#if !defined(ENABLE_DEBUG_LOGGING) + if (mode & LIBUSB_LOG_CB_CONTEXT) { + ctx = usbi_get_context(ctx); + ctx->log_handler = cb; + } +#else + UNUSED(ctx); +#endif +#else + UNUSED(ctx); + UNUSED(cb); + UNUSED(mode); +#endif +} + /** \ingroup libusb_lib * Set an option in the library. * @@ -2043,6 +2180,7 @@ void API_EXPORTED libusb_set_debug(libusb_context *ctx, int level) * \returns LIBUSB_ERROR_INVALID_PARAM if the option or arguments are invalid * \returns LIBUSB_ERROR_NOT_SUPPORTED if the option is valid but not supported * on this platform + * \returns LIBUSB_ERROR_NOT_FOUND if LIBUSB_OPTION_USE_USBDK is valid on this platform but UsbDk is not available */ int API_EXPORTED libusb_set_option(libusb_context *ctx, enum libusb_option option, ...) @@ -2050,7 +2188,7 @@ int API_EXPORTED libusb_set_option(libusb_context *ctx, int arg, r = LIBUSB_SUCCESS; va_list ap; - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); va_start(ap, option); switch (option) { @@ -2068,6 +2206,7 @@ int API_EXPORTED libusb_set_option(libusb_context *ctx, /* Handle all backend-specific options here */ case LIBUSB_OPTION_USE_USBDK: + case LIBUSB_OPTION_WEAK_AUTHORITY: if (usbi_backend.set_option) r = usbi_backend.set_option(ctx, option, ap); else @@ -2125,9 +2264,8 @@ int API_EXPORTED libusb_init(libusb_context **context) usbi_mutex_static_lock(&default_context_lock); - if (!timestamp_origin.tv_sec) { - usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, ×tamp_origin); - } + if (!timestamp_origin.tv_sec) + usbi_get_monotonic_time(×tamp_origin); if (!context && usbi_default_context) { usbi_dbg("reusing default context"); @@ -2136,7 +2274,7 @@ int API_EXPORTED libusb_init(libusb_context **context) return 0; } - ctx = calloc(1, sizeof(*ctx) + priv_size); + ctx = calloc(1, PTR_ALIGN(sizeof(*ctx)) + priv_size); if (!ctx) { r = LIBUSB_ERROR_NO_MEM; goto err_unlock; @@ -2169,7 +2307,7 @@ int API_EXPORTED libusb_init(libusb_context **context) usbi_mutex_static_lock(&active_contexts_lock); if (first_init) { first_init = 0; - list_init (&active_contexts_list); + list_init(&active_contexts_list); } list_add (&ctx->list, &active_contexts_list); usbi_mutex_static_unlock(&active_contexts_lock); @@ -2201,11 +2339,11 @@ err_free_ctx: } usbi_mutex_static_lock(&active_contexts_lock); - list_del (&ctx->list); + list_del(&ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); usbi_mutex_lock(&ctx->usb_devs_lock); - list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) { + for_each_device_safe(ctx, dev, next) { list_del(&dev->list); libusb_unref_device(dev); } @@ -2226,30 +2364,47 @@ err_unlock: * before your application terminates. * \param ctx the context to deinitialize, or NULL for the default context */ -void API_EXPORTED libusb_exit(struct libusb_context *ctx) +void API_EXPORTED libusb_exit(libusb_context *ctx) { struct libusb_device *dev, *next; struct timeval tv = { 0, 0 }; + int destroying_default_context = 0; - usbi_dbg(""); - USBI_GET_CONTEXT(ctx); + usbi_dbg(" "); + + ctx = usbi_get_context(ctx); /* if working with default context, only actually do the deinitialization * if we're the last user */ usbi_mutex_static_lock(&default_context_lock); if (ctx == usbi_default_context) { + if (!usbi_default_context) { + usbi_dbg("no default context, not initialized?"); + usbi_mutex_static_unlock(&default_context_lock); + return; + } + if (--default_context_refcnt > 0) { usbi_dbg("not destroying default context"); usbi_mutex_static_unlock(&default_context_lock); return; } usbi_dbg("destroying default context"); - usbi_default_context = NULL; + + /* + * Setting this flag without unlocking the default context, as + * we are actually destroying the default context. + * usbi_default_context is not set to NULL yet, as all activities + * would only stop after usbi_backend->exit() returns. + */ + destroying_default_context = 1; + } else { + /* Unlock default context, as we're not modifying it. */ + usbi_mutex_static_unlock(&default_context_lock); } - usbi_mutex_static_unlock(&default_context_lock); usbi_mutex_static_lock(&active_contexts_lock); - list_del (&ctx->list); + list_del(&ctx->list); usbi_mutex_static_unlock(&active_contexts_lock); if (libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { @@ -2268,7 +2423,7 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx) libusb_handle_events_timeout(ctx, &tv); usbi_mutex_lock(&ctx->usb_devs_lock); - list_for_each_entry_safe(dev, next, &ctx->usb_devs, list, struct libusb_device) { + for_each_device_safe(ctx, dev, next) { list_del(&dev->list); libusb_unref_device(dev); } @@ -2290,6 +2445,11 @@ void API_EXPORTED libusb_exit(struct libusb_context *ctx) usbi_mutex_destroy(&ctx->usb_devs_lock); usbi_mutex_destroy(&ctx->hotplug_cbs_lock); free(ctx); + + if (destroying_default_context) { + usbi_default_context = NULL; + usbi_mutex_static_unlock(&default_context_lock); + } } /** \ingroup libusb_misc @@ -2321,89 +2481,90 @@ int API_EXPORTED libusb_has_capability(uint32_t capability) #ifdef LIBUSB_PRINTF_WIN32 /* * Prior to VS2015, Microsoft did not provide the snprintf() function and - * provided a vsnprintf() that did not guarantee NULL-terminated output. + * provided a vsnprintf() that did not guarantee NUL-terminated output. * Microsoft did provide a _snprintf() function, but again it did not * guarantee NULL-terminated output. * - * The below implementations guarantee NULL-terminated output and are + * The below implementations guarantee NUL-terminated output and are * C99 compliant. */ int usbi_snprintf(char *str, size_t size, const char *format, ...) { - va_list ap; + va_list args; int ret; - va_start(ap, format); - ret = usbi_vsnprintf(str, size, format, ap); - va_end(ap); + va_start(args, format); + ret = usbi_vsnprintf(str, size, format, args); + va_end(args); return ret; } -int usbi_vsnprintf(char *str, size_t size, const char *format, va_list ap) +int usbi_vsnprintf(char *str, size_t size, const char *format, va_list args) { int ret; - ret = _vsnprintf(str, size, format, ap); + ret = _vsnprintf(str, size, format, args); if (ret < 0 || ret == (int)size) { - /* Output is truncated, ensure buffer is NULL-terminated and + /* Output is truncated, ensure buffer is NUL-terminated and * determine how many characters would have been written. */ str[size - 1] = '\0'; if (ret < 0) - ret = _vsnprintf(NULL, 0, format, ap); + ret = _vsnprintf(NULL, 0, format, args); } return ret; } #endif /* LIBUSB_PRINTF_WIN32 */ -static void usbi_log_str(enum libusb_log_level level, const char *str) +static void log_str(enum libusb_log_level level, const char *str) { #if defined(USE_SYSTEM_LOGGING_FACILITY) -#if defined(OS_WINDOWS) - OutputDebugString(str); -#elif defined(OS_WINCE) - /* Windows CE only supports the Unicode version of OutputDebugString. */ - WCHAR wbuf[USBI_MAX_LOG_LEN]; - MultiByteToWideChar(CP_UTF8, 0, str, -1, wbuf, sizeof(wbuf)); - OutputDebugStringW(wbuf); -#elif defined(__ANDROID__) - int priority = ANDROID_LOG_UNKNOWN; +#if defined(__ANDROID__) + int priority; switch (level) { - case LIBUSB_LOG_LEVEL_NONE: return; + case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */ case LIBUSB_LOG_LEVEL_ERROR: priority = ANDROID_LOG_ERROR; break; case LIBUSB_LOG_LEVEL_WARNING: priority = ANDROID_LOG_WARN; break; case LIBUSB_LOG_LEVEL_INFO: priority = ANDROID_LOG_INFO; break; case LIBUSB_LOG_LEVEL_DEBUG: priority = ANDROID_LOG_DEBUG; break; + default: priority = ANDROID_LOG_UNKNOWN; } __android_log_write(priority, "libusb", str); -#elif defined(HAVE_SYSLOG_FUNC) - int syslog_level = LOG_INFO; +#elif defined(_WIN32) + UNUSED(level); + OutputDebugStringA(str); +#elif defined(HAVE_SYSLOG) + int syslog_level; switch (level) { - case LIBUSB_LOG_LEVEL_NONE: return; + case LIBUSB_LOG_LEVEL_NONE: return; /* Impossible, but keeps compiler happy */ case LIBUSB_LOG_LEVEL_ERROR: syslog_level = LOG_ERR; break; case LIBUSB_LOG_LEVEL_WARNING: syslog_level = LOG_WARNING; break; case LIBUSB_LOG_LEVEL_INFO: syslog_level = LOG_INFO; break; case LIBUSB_LOG_LEVEL_DEBUG: syslog_level = LOG_DEBUG; break; + default: syslog_level = LOG_INFO; } syslog(syslog_level, "%s", str); -#else /* All of gcc, Clang, XCode seem to use #warning */ +#else /* All of gcc, Clang, Xcode seem to use #warning */ #warning System logging is not supported on this platform. Logging to stderr will be used instead. + UNUSED(level); fputs(str, stderr); #endif #else - fputs(str, stderr); + /* Global log handler */ + if (log_handler) + log_handler(NULL, level, str); + else + fputs(str, stderr); #endif /* USE_SYSTEM_LOGGING_FACILITY */ - UNUSED(level); } -void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, +static void log_v(struct libusb_context *ctx, enum libusb_log_level level, const char *function, const char *format, va_list args) { const char *prefix; char buf[USBI_MAX_LOG_LEN]; - struct timespec now; int global_debug, header_len, text_len; static int has_debug_header_been_displayed = 0; @@ -2411,41 +2572,22 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, global_debug = 1; UNUSED(ctx); #else - enum libusb_log_level ctx_level = LIBUSB_LOG_LEVEL_NONE; + enum libusb_log_level ctx_level; - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); if (ctx) ctx_level = ctx->debug; else ctx_level = get_env_debug_level(); - if (ctx_level == LIBUSB_LOG_LEVEL_NONE) - return; - if (level == LIBUSB_LOG_LEVEL_WARNING && ctx_level < LIBUSB_LOG_LEVEL_WARNING) - return; - if (level == LIBUSB_LOG_LEVEL_INFO && ctx_level < LIBUSB_LOG_LEVEL_INFO) - return; - if (level == LIBUSB_LOG_LEVEL_DEBUG && ctx_level < LIBUSB_LOG_LEVEL_DEBUG) + if (ctx_level < level) return; global_debug = (ctx_level == LIBUSB_LOG_LEVEL_DEBUG); #endif - usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &now); - if ((global_debug) && (!has_debug_header_been_displayed)) { - has_debug_header_been_displayed = 1; - usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] " USBI_LOG_LINE_END); - usbi_log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END); - } - if (now.tv_nsec < timestamp_origin.tv_nsec) { - now.tv_sec--; - now.tv_nsec += 1000000000L; - } - now.tv_sec -= timestamp_origin.tv_sec; - now.tv_nsec -= timestamp_origin.tv_nsec; - switch (level) { - case LIBUSB_LOG_LEVEL_NONE: + case LIBUSB_LOG_LEVEL_NONE: /* Impossible, but keeps compiler happy */ return; case LIBUSB_LOG_LEVEL_ERROR: prefix = "error"; @@ -2465,35 +2607,51 @@ void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, } if (global_debug) { + struct timespec timestamp; + + if (!has_debug_header_been_displayed) { + has_debug_header_been_displayed = 1; + log_str(LIBUSB_LOG_LEVEL_DEBUG, "[timestamp] [threadID] facility level [function call] " USBI_LOG_LINE_END); + log_str(LIBUSB_LOG_LEVEL_DEBUG, "--------------------------------------------------------------------------------" USBI_LOG_LINE_END); + } + + usbi_get_monotonic_time(×tamp); + TIMESPEC_SUB(×tamp, ×tamp_origin, ×tamp); + header_len = snprintf(buf, sizeof(buf), - "[%2d.%06d] [%08x] libusb: %s [%s] ", - (int)now.tv_sec, (int)(now.tv_nsec / 1000L), usbi_get_tid(), prefix, function); + "[%2ld.%06ld] [%08x] libusb: %s [%s] ", + (long)timestamp.tv_sec, (long)(timestamp.tv_nsec / 1000L), usbi_get_tid(), prefix, function); } else { header_len = snprintf(buf, sizeof(buf), "libusb: %s [%s] ", prefix, function); } if (header_len < 0 || header_len >= (int)sizeof(buf)) { - /* Somehow snprintf failed to write to the buffer, + /* Somehow snprintf() failed to write to the buffer, * remove the header so something useful is output. */ header_len = 0; } - /* Make sure buffer is NUL terminated */ - buf[header_len] = '\0'; - text_len = vsnprintf(buf + header_len, sizeof(buf) - header_len, + + text_len = vsnprintf(buf + header_len, sizeof(buf) - (size_t)header_len, format, args); if (text_len < 0 || text_len + header_len >= (int)sizeof(buf)) { /* Truncated log output. On some platforms a -1 return value means * that the output was truncated. */ - text_len = sizeof(buf) - header_len; + text_len = (int)sizeof(buf) - header_len; } - if (header_len + text_len + sizeof(USBI_LOG_LINE_END) >= sizeof(buf)) { + if (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END) >= (int)sizeof(buf)) { /* Need to truncate the text slightly to fit on the terminator. */ - text_len -= (header_len + text_len + sizeof(USBI_LOG_LINE_END)) - sizeof(buf); + text_len -= (header_len + text_len + (int)sizeof(USBI_LOG_LINE_END)) - (int)sizeof(buf); } strcpy(buf + header_len + text_len, USBI_LOG_LINE_END); - usbi_log_str(level, buf); + log_str(level, buf); + + /* Per-context log handler */ +#ifndef ENABLE_DEBUG_LOGGING + if (ctx && ctx->log_handler) + ctx->log_handler(ctx, level, buf); +#endif } void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, @@ -2501,9 +2659,9 @@ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, { va_list args; - va_start (args, format); - usbi_log_v(ctx, level, function, format, args); - va_end (args); + va_start(args, format); + log_v(ctx, level, function, format, args); + va_end(args); } #endif /* ENABLE_LOGGING */ diff --git a/mac/libusb/descriptor.c b/mac/libusb/descriptor.c index 74d6de557..ecd944180 100644 --- a/mac/libusb/descriptor.c +++ b/mac/libusb/descriptor.c @@ -19,91 +19,73 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "libusbi.h" -#include -#include -#include #include -#include "libusbi.h" - -#define DESC_HEADER_LENGTH 2 -#define DEVICE_DESC_LENGTH 18 -#define CONFIG_DESC_LENGTH 9 -#define INTERFACE_DESC_LENGTH 9 -#define ENDPOINT_DESC_LENGTH 7 -#define ENDPOINT_AUDIO_DESC_LENGTH 9 +#define DESC_HEADER_LENGTH 2 /** @defgroup libusb_desc USB descriptors * This page details how to examine the various standard USB descriptors * for detected devices */ -/* set host_endian if the w values are already in host endian format, - * as opposed to bus endian. */ -int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, - void *dest, int host_endian) +#define READ_LE16(p) ((uint16_t) \ + (((uint16_t)((p)[1]) << 8) | \ + ((uint16_t)((p)[0])))) + +#define READ_LE32(p) ((uint32_t) \ + (((uint32_t)((p)[3]) << 24) | \ + ((uint32_t)((p)[2]) << 16) | \ + ((uint32_t)((p)[1]) << 8) | \ + ((uint32_t)((p)[0])))) + +static void parse_descriptor(const void *source, const char *descriptor, void *dest) { - const unsigned char *sp = source; - unsigned char *dp = dest; - uint16_t w; - const char *cp; - uint32_t d; - - for (cp = descriptor; *cp; cp++) { - switch (*cp) { - case 'b': /* 8-bit byte */ - *dp++ = *sp++; - break; - case 'w': /* 16-bit word, convert from little endian to CPU */ - dp += ((uintptr_t)dp & 1); /* Align to word boundary */ - - if (host_endian) { - memcpy(dp, sp, 2); - } else { - w = (sp[1] << 8) | sp[0]; - *((uint16_t *)dp) = w; - } - sp += 2; - dp += 2; - break; - case 'd': /* 32-bit word, convert from little endian to CPU */ - dp += ((uintptr_t)dp & 1); /* Align to word boundary */ - - if (host_endian) { - memcpy(dp, sp, 4); - } else { - d = (sp[3] << 24) | (sp[2] << 16) | - (sp[1] << 8) | sp[0]; - *((uint32_t *)dp) = d; - } - sp += 4; - dp += 4; - break; - case 'u': /* 16 byte UUID */ - memcpy(dp, sp, 16); - sp += 16; - dp += 16; - break; + const uint8_t *sp = source; + uint8_t *dp = dest; + char field_type; + + while (*descriptor) { + field_type = *descriptor++; + switch (field_type) { + case 'b': /* 8-bit byte */ + *dp++ = *sp++; + break; + case 'w': /* 16-bit word, convert from little endian to CPU */ + dp += ((uintptr_t)dp & 1); /* Align to 16-bit word boundary */ + + *((uint16_t *)dp) = READ_LE16(sp); + sp += 2; + dp += 2; + break; + case 'd': /* 32-bit word, convert from little endian to CPU */ + dp += 4 - ((uintptr_t)dp & 3); /* Align to 32-bit word boundary */ + + *((uint32_t *)dp) = READ_LE32(sp); + sp += 4; + dp += 4; + break; + case 'u': /* 16 byte UUID */ + memcpy(dp, sp, 16); + sp += 16; + dp += 16; + break; } } - - return (int) (sp - source); } static void clear_endpoint(struct libusb_endpoint_descriptor *endpoint) { - free((void *) endpoint->extra); + free((void *)endpoint->extra); } static int parse_endpoint(struct libusb_context *ctx, - struct libusb_endpoint_descriptor *endpoint, unsigned char *buffer, - int size, int host_endian) + struct libusb_endpoint_descriptor *endpoint, const uint8_t *buffer, int size) { - struct usb_descriptor_header header; - unsigned char *extra; - unsigned char *begin; + const struct usbi_descriptor_header *header; + const uint8_t *begin; + void *extra; int parsed = 0; int len; @@ -113,75 +95,69 @@ static int parse_endpoint(struct libusb_context *ctx, return LIBUSB_ERROR_IO; } - usbi_parse_descriptor(buffer, "bb", &header, 0); - if (header.bDescriptorType != LIBUSB_DT_ENDPOINT) { - usbi_err(ctx, "unexpected descriptor %x (expected %x)", - header.bDescriptorType, LIBUSB_DT_ENDPOINT); + header = (const struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_ENDPOINT) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + header->bDescriptorType, LIBUSB_DT_ENDPOINT); return parsed; - } - if (header.bLength > size) { - usbi_warn(ctx, "short endpoint descriptor read %d/%d", - size, header.bLength); - return parsed; - } - if (header.bLength >= ENDPOINT_AUDIO_DESC_LENGTH) - usbi_parse_descriptor(buffer, "bbbbwbbb", endpoint, host_endian); - else if (header.bLength >= ENDPOINT_DESC_LENGTH) - usbi_parse_descriptor(buffer, "bbbbwb", endpoint, host_endian); - else { - usbi_err(ctx, "invalid endpoint bLength (%d)", header.bLength); + } else if (header->bLength < LIBUSB_DT_ENDPOINT_SIZE) { + usbi_err(ctx, "invalid endpoint bLength (%u)", header->bLength); return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_warn(ctx, "short endpoint descriptor read %d/%u", + size, header->bLength); + return parsed; } - buffer += header.bLength; - size -= header.bLength; - parsed += header.bLength; + if (header->bLength >= LIBUSB_DT_ENDPOINT_AUDIO_SIZE) + parse_descriptor(buffer, "bbbbwbbb", endpoint); + else + parse_descriptor(buffer, "bbbbwb", endpoint); + + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; /* Skip over the rest of the Class Specific or Vendor Specific */ /* descriptors */ begin = buffer; while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header, 0); - if (header.bLength < DESC_HEADER_LENGTH) { - usbi_err(ctx, "invalid extra ep desc len (%d)", - header.bLength); + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid extra ep desc len (%u)", + header->bLength); return LIBUSB_ERROR_IO; - } else if (header.bLength > size) { - usbi_warn(ctx, "short extra ep desc read %d/%d", - size, header.bLength); + } else if (header->bLength > size) { + usbi_warn(ctx, "short extra ep desc read %d/%u", + size, header->bLength); return parsed; } /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) || - (header.bDescriptorType == LIBUSB_DT_INTERFACE) || - (header.bDescriptorType == LIBUSB_DT_CONFIG) || - (header.bDescriptorType == LIBUSB_DT_DEVICE)) + if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) break; - usbi_dbg("skipping descriptor %x", header.bDescriptorType); - buffer += header.bLength; - size -= header.bLength; - parsed += header.bLength; + usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType); + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; } /* Copy any unknown descriptors into a storage area for drivers */ /* to later parse */ len = (int)(buffer - begin); - if (!len) { - endpoint->extra = NULL; - endpoint->extra_length = 0; + if (len <= 0) return parsed; - } - extra = malloc(len); - endpoint->extra = extra; - if (!extra) { - endpoint->extra_length = 0; + extra = malloc((size_t)len); + if (!extra) return LIBUSB_ERROR_NO_MEM; - } memcpy(extra, begin, len); + endpoint->extra = extra; endpoint->extra_length = len; return parsed; @@ -190,47 +166,45 @@ static int parse_endpoint(struct libusb_context *ctx, static void clear_interface(struct libusb_interface *usb_interface) { int i; - int j; if (usb_interface->altsetting) { for (i = 0; i < usb_interface->num_altsetting; i++) { struct libusb_interface_descriptor *ifp = (struct libusb_interface_descriptor *) usb_interface->altsetting + i; - free((void *) ifp->extra); + + free((void *)ifp->extra); if (ifp->endpoint) { + uint8_t j; + for (j = 0; j < ifp->bNumEndpoints; j++) clear_endpoint((struct libusb_endpoint_descriptor *) ifp->endpoint + j); } - free((void *) ifp->endpoint); + free((void *)ifp->endpoint); } } - free((void *) usb_interface->altsetting); + free((void *)usb_interface->altsetting); usb_interface->altsetting = NULL; } static int parse_interface(libusb_context *ctx, - struct libusb_interface *usb_interface, unsigned char *buffer, int size, - int host_endian) + struct libusb_interface *usb_interface, const uint8_t *buffer, int size) { - int i; int len; int r; int parsed = 0; int interface_number = -1; - struct usb_descriptor_header header; + const struct usbi_descriptor_header *header; + const struct usbi_interface_descriptor *if_desc; struct libusb_interface_descriptor *ifp; - unsigned char *begin; + const uint8_t *begin; - usb_interface->num_altsetting = 0; + while (size >= LIBUSB_DT_INTERFACE_SIZE) { + struct libusb_interface_descriptor *altsetting; - while (size >= INTERFACE_DESC_LENGTH) { - struct libusb_interface_descriptor *altsetting = - (struct libusb_interface_descriptor *) usb_interface->altsetting; - altsetting = usbi_reallocf(altsetting, - sizeof(struct libusb_interface_descriptor) * - (usb_interface->num_altsetting + 1)); + altsetting = realloc((void *)usb_interface->altsetting, + sizeof(*altsetting) * (size_t)(usb_interface->num_altsetting + 1)); if (!altsetting) { r = LIBUSB_ERROR_NO_MEM; goto err; @@ -238,25 +212,22 @@ static int parse_interface(libusb_context *ctx, usb_interface->altsetting = altsetting; ifp = altsetting + usb_interface->num_altsetting; - usbi_parse_descriptor(buffer, "bbbbbbbbb", ifp, 0); + parse_descriptor(buffer, "bbbbbbbbb", ifp); if (ifp->bDescriptorType != LIBUSB_DT_INTERFACE) { - usbi_err(ctx, "unexpected descriptor %x (expected %x)", + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", ifp->bDescriptorType, LIBUSB_DT_INTERFACE); return parsed; - } - if (ifp->bLength < INTERFACE_DESC_LENGTH) { - usbi_err(ctx, "invalid interface bLength (%d)", + } else if (ifp->bLength < LIBUSB_DT_INTERFACE_SIZE) { + usbi_err(ctx, "invalid interface bLength (%u)", ifp->bLength); r = LIBUSB_ERROR_IO; goto err; - } - if (ifp->bLength > size) { - usbi_warn(ctx, "short intf descriptor read %d/%d", + } else if (ifp->bLength > size) { + usbi_warn(ctx, "short intf descriptor read %d/%u", size, ifp->bLength); return parsed; - } - if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { - usbi_err(ctx, "too many endpoints (%d)", ifp->bNumEndpoints); + } else if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { + usbi_err(ctx, "too many endpoints (%u)", ifp->bNumEndpoints); r = LIBUSB_ERROR_IO; goto err; } @@ -278,61 +249,65 @@ static int parse_interface(libusb_context *ctx, /* Skip over any interface, class or vendor descriptors */ while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header, 0); - if (header.bLength < DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { usbi_err(ctx, - "invalid extra intf desc len (%d)", - header.bLength); + "invalid extra intf desc len (%u)", + header->bLength); r = LIBUSB_ERROR_IO; goto err; - } else if (header.bLength > size) { + } else if (header->bLength > size) { usbi_warn(ctx, - "short extra intf desc read %d/%d", - size, header.bLength); + "short extra intf desc read %d/%u", + size, header->bLength); return parsed; } /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == LIBUSB_DT_INTERFACE) || - (header.bDescriptorType == LIBUSB_DT_ENDPOINT) || - (header.bDescriptorType == LIBUSB_DT_CONFIG) || - (header.bDescriptorType == LIBUSB_DT_DEVICE)) + if (header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) break; - buffer += header.bLength; - parsed += header.bLength; - size -= header.bLength; + buffer += header->bLength; + parsed += header->bLength; + size -= header->bLength; } /* Copy any unknown descriptors into a storage area for */ /* drivers to later parse */ len = (int)(buffer - begin); - if (len) { - ifp->extra = malloc(len); - if (!ifp->extra) { + if (len > 0) { + void *extra = malloc((size_t)len); + + if (!extra) { r = LIBUSB_ERROR_NO_MEM; goto err; } - memcpy((unsigned char *) ifp->extra, begin, len); + + memcpy(extra, begin, len); + ifp->extra = extra; ifp->extra_length = len; } if (ifp->bNumEndpoints > 0) { struct libusb_endpoint_descriptor *endpoint; - endpoint = calloc(ifp->bNumEndpoints, sizeof(struct libusb_endpoint_descriptor)); - ifp->endpoint = endpoint; + uint8_t i; + + endpoint = calloc(ifp->bNumEndpoints, sizeof(*endpoint)); if (!endpoint) { r = LIBUSB_ERROR_NO_MEM; goto err; } + ifp->endpoint = endpoint; for (i = 0; i < ifp->bNumEndpoints; i++) { - r = parse_endpoint(ctx, endpoint + i, buffer, size, - host_endian); + r = parse_endpoint(ctx, endpoint + i, buffer, size); if (r < 0) goto err; if (r == 0) { - ifp->bNumEndpoints = (uint8_t)i; + ifp->bNumEndpoints = i; break; } @@ -343,10 +318,10 @@ static int parse_interface(libusb_context *ctx, } /* We check to see if it's an alternate to this one */ - ifp = (struct libusb_interface_descriptor *) buffer; + if_desc = (const struct usbi_interface_descriptor *)buffer; if (size < LIBUSB_DT_INTERFACE_SIZE || - ifp->bDescriptorType != LIBUSB_DT_INTERFACE || - ifp->bInterfaceNumber != interface_number) + if_desc->bDescriptorType != LIBUSB_DT_INTERFACE || + if_desc->bInterfaceNumber != interface_number) return parsed; } @@ -358,23 +333,23 @@ err: static void clear_configuration(struct libusb_config_descriptor *config) { - int i; + uint8_t i; + if (config->interface) { for (i = 0; i < config->bNumInterfaces; i++) clear_interface((struct libusb_interface *) config->interface + i); } - free((void *) config->interface); - free((void *) config->extra); + free((void *)config->interface); + free((void *)config->extra); } static int parse_configuration(struct libusb_context *ctx, - struct libusb_config_descriptor *config, unsigned char *buffer, - int size, int host_endian) + struct libusb_config_descriptor *config, const uint8_t *buffer, int size) { - int i; + uint8_t i; int r; - struct usb_descriptor_header header; + const struct usbi_descriptor_header *header; struct libusb_interface *usb_interface; if (size < LIBUSB_DT_CONFIG_SIZE) { @@ -383,95 +358,89 @@ static int parse_configuration(struct libusb_context *ctx, return LIBUSB_ERROR_IO; } - usbi_parse_descriptor(buffer, "bbwbbbbb", config, host_endian); + parse_descriptor(buffer, "bbwbbbbb", config); if (config->bDescriptorType != LIBUSB_DT_CONFIG) { - usbi_err(ctx, "unexpected descriptor %x (expected %x)", + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", config->bDescriptorType, LIBUSB_DT_CONFIG); return LIBUSB_ERROR_IO; - } - if (config->bLength < LIBUSB_DT_CONFIG_SIZE) { - usbi_err(ctx, "invalid config bLength (%d)", config->bLength); + } else if (config->bLength < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(ctx, "invalid config bLength (%u)", config->bLength); return LIBUSB_ERROR_IO; - } - if (config->bLength > size) { - usbi_err(ctx, "short config descriptor read %d/%d", + } else if (config->bLength > size) { + usbi_err(ctx, "short config descriptor read %d/%u", size, config->bLength); return LIBUSB_ERROR_IO; - } - if (config->bNumInterfaces > USB_MAXINTERFACES) { - usbi_err(ctx, "too many interfaces (%d)", config->bNumInterfaces); + } else if (config->bNumInterfaces > USB_MAXINTERFACES) { + usbi_err(ctx, "too many interfaces (%u)", config->bNumInterfaces); return LIBUSB_ERROR_IO; } - usb_interface = calloc(config->bNumInterfaces, sizeof(struct libusb_interface)); - config->interface = usb_interface; + usb_interface = calloc(config->bNumInterfaces, sizeof(*usb_interface)); if (!usb_interface) return LIBUSB_ERROR_NO_MEM; + config->interface = usb_interface; + buffer += config->bLength; size -= config->bLength; - config->extra = NULL; - config->extra_length = 0; - for (i = 0; i < config->bNumInterfaces; i++) { int len; - unsigned char *begin; + const uint8_t *begin; /* Skip over the rest of the Class Specific or Vendor */ /* Specific descriptors */ begin = buffer; while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header, 0); - - if (header.bLength < DESC_HEADER_LENGTH) { + header = (const struct usbi_descriptor_header *)buffer; + if (header->bLength < DESC_HEADER_LENGTH) { usbi_err(ctx, - "invalid extra config desc len (%d)", - header.bLength); + "invalid extra config desc len (%u)", + header->bLength); r = LIBUSB_ERROR_IO; goto err; - } else if (header.bLength > size) { + } else if (header->bLength > size) { usbi_warn(ctx, - "short extra config desc read %d/%d", - size, header.bLength); - config->bNumInterfaces = (uint8_t)i; + "short extra config desc read %d/%u", + size, header->bLength); + config->bNumInterfaces = i; return size; } /* If we find another "proper" descriptor then we're done */ - if ((header.bDescriptorType == LIBUSB_DT_ENDPOINT) || - (header.bDescriptorType == LIBUSB_DT_INTERFACE) || - (header.bDescriptorType == LIBUSB_DT_CONFIG) || - (header.bDescriptorType == LIBUSB_DT_DEVICE)) + if (header->bDescriptorType == LIBUSB_DT_ENDPOINT || + header->bDescriptorType == LIBUSB_DT_INTERFACE || + header->bDescriptorType == LIBUSB_DT_CONFIG || + header->bDescriptorType == LIBUSB_DT_DEVICE) break; - usbi_dbg("skipping descriptor 0x%x", header.bDescriptorType); - buffer += header.bLength; - size -= header.bLength; + usbi_dbg("skipping descriptor 0x%x", header->bDescriptorType); + buffer += header->bLength; + size -= header->bLength; } /* Copy any unknown descriptors into a storage area for */ /* drivers to later parse */ len = (int)(buffer - begin); - if (len) { - /* FIXME: We should realloc and append here */ - if (!config->extra_length) { - config->extra = malloc(len); - if (!config->extra) { - r = LIBUSB_ERROR_NO_MEM; - goto err; - } + if (len > 0) { + uint8_t *extra = realloc((void *)config->extra, + (size_t)(config->extra_length + len)); - memcpy((unsigned char *) config->extra, begin, len); - config->extra_length = len; + if (!extra) { + r = LIBUSB_ERROR_NO_MEM; + goto err; } + + memcpy(extra + config->extra_length, begin, len); + config->extra = extra; + config->extra_length += len; } - r = parse_interface(ctx, usb_interface + i, buffer, size, host_endian); + r = parse_interface(ctx, usb_interface + i, buffer, size); if (r < 0) goto err; if (r == 0) { - config->bNumInterfaces = (uint8_t)i; + config->bNumInterfaces = i; break; } @@ -487,16 +456,15 @@ err: } static int raw_desc_to_config(struct libusb_context *ctx, - unsigned char *buf, int size, int host_endian, - struct libusb_config_descriptor **config) + const uint8_t *buf, int size, struct libusb_config_descriptor **config) { - struct libusb_config_descriptor *_config = malloc(sizeof(*_config)); + struct libusb_config_descriptor *_config = calloc(1, sizeof(*_config)); int r; - + if (!_config) return LIBUSB_ERROR_NO_MEM; - r = parse_configuration(ctx, _config, buf, size, host_endian); + r = parse_configuration(ctx, _config, buf, size); if (r < 0) { usbi_err(ctx, "parse_configuration failed with error %d", r); free(_config); @@ -504,28 +472,48 @@ static int raw_desc_to_config(struct libusb_context *ctx, } else if (r > 0) { usbi_warn(ctx, "still %d bytes of descriptor data left", r); } - + *config = _config; return LIBUSB_SUCCESS; } -int usbi_device_cache_descriptor(libusb_device *dev) +static int get_active_config_descriptor(struct libusb_device *dev, + uint8_t *buffer, size_t size) { - int r, host_endian = 0; + int r = usbi_backend.get_active_config_descriptor(dev, buffer, size); - r = usbi_backend.get_device_descriptor(dev, (unsigned char *) &dev->device_descriptor, - &host_endian); if (r < 0) return r; - if (!host_endian) { - dev->device_descriptor.bcdUSB = libusb_le16_to_cpu(dev->device_descriptor.bcdUSB); - dev->device_descriptor.idVendor = libusb_le16_to_cpu(dev->device_descriptor.idVendor); - dev->device_descriptor.idProduct = libusb_le16_to_cpu(dev->device_descriptor.idProduct); - dev->device_descriptor.bcdDevice = libusb_le16_to_cpu(dev->device_descriptor.bcdDevice); + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } else if (r != (int)size) { + usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, (int)size); } - return LIBUSB_SUCCESS; + return r; +} + +static int get_config_descriptor(struct libusb_device *dev, uint8_t config_idx, + uint8_t *buffer, size_t size) +{ + int r = usbi_backend.get_config_descriptor(dev, config_idx, buffer, size); + + if (r < 0) + return r; + if (r < LIBUSB_DT_CONFIG_SIZE) { + usbi_err(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, LIBUSB_DT_CONFIG_SIZE); + return LIBUSB_ERROR_IO; + } else if (r != (int)size) { + usbi_warn(DEVICE_CTX(dev), "short config descriptor read %d/%d", + r, (int)size); + } + + return r; } /** \ingroup libusb_desc @@ -543,9 +531,10 @@ int usbi_device_cache_descriptor(libusb_device *dev) int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, struct libusb_device_descriptor *desc) { - usbi_dbg(""); - memcpy((unsigned char *) desc, (unsigned char *) &dev->device_descriptor, - sizeof (dev->device_descriptor)); + usbi_dbg(" "); + static_assert(sizeof(dev->device_descriptor) == LIBUSB_DT_DEVICE_SIZE, + "struct libusb_device_descriptor is not expected size"); + *desc = dev->device_descriptor; return 0; } @@ -566,31 +555,23 @@ int API_EXPORTED libusb_get_device_descriptor(libusb_device *dev, int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, struct libusb_config_descriptor **config) { - struct libusb_config_descriptor _config; - unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; - unsigned char *buf = NULL; - int host_endian = 0; + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; int r; - r = usbi_backend.get_active_config_descriptor(dev, tmp, - LIBUSB_DT_CONFIG_SIZE, &host_endian); + r = get_active_config_descriptor(dev, _config.buf, sizeof(_config.buf)); if (r < 0) return r; - if (r < LIBUSB_DT_CONFIG_SIZE) { - usbi_err(dev->ctx, "short config descriptor read %d/%d", - r, LIBUSB_DT_CONFIG_SIZE); - return LIBUSB_ERROR_IO; - } - usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); - buf = malloc(_config.wTotalLength); + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); if (!buf) return LIBUSB_ERROR_NO_MEM; - r = usbi_backend.get_active_config_descriptor(dev, buf, - _config.wTotalLength, &host_endian); + r = get_active_config_descriptor(dev, buf, config_len); if (r >= 0) - r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); free(buf); return r; @@ -615,70 +596,32 @@ int API_EXPORTED libusb_get_active_config_descriptor(libusb_device *dev, int API_EXPORTED libusb_get_config_descriptor(libusb_device *dev, uint8_t config_index, struct libusb_config_descriptor **config) { - struct libusb_config_descriptor _config; - unsigned char tmp[LIBUSB_DT_CONFIG_SIZE]; - unsigned char *buf = NULL; - int host_endian = 0; + union usbi_config_desc_buf _config; + uint16_t config_len; + uint8_t *buf; int r; - usbi_dbg("index %d", config_index); - if (config_index >= dev->num_configurations) + usbi_dbg("index %u", config_index); + if (config_index >= dev->device_descriptor.bNumConfigurations) return LIBUSB_ERROR_NOT_FOUND; - r = usbi_backend.get_config_descriptor(dev, config_index, tmp, - LIBUSB_DT_CONFIG_SIZE, &host_endian); + r = get_config_descriptor(dev, config_index, _config.buf, sizeof(_config.buf)); if (r < 0) return r; - if (r < LIBUSB_DT_CONFIG_SIZE) { - usbi_err(dev->ctx, "short config descriptor read %d/%d", - r, LIBUSB_DT_CONFIG_SIZE); - return LIBUSB_ERROR_IO; - } - usbi_parse_descriptor(tmp, "bbw", &_config, host_endian); - buf = malloc(_config.wTotalLength); + config_len = libusb_le16_to_cpu(_config.desc.wTotalLength); + buf = malloc(config_len); if (!buf) return LIBUSB_ERROR_NO_MEM; - r = usbi_backend.get_config_descriptor(dev, config_index, buf, - _config.wTotalLength, &host_endian); + r = get_config_descriptor(dev, config_index, buf, config_len); if (r >= 0) - r = raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + r = raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); free(buf); return r; } -/* iterate through all configurations, returning the index of the configuration - * matching a specific bConfigurationValue in the idx output parameter, or -1 - * if the config was not found. - * returns 0 on success or a LIBUSB_ERROR code - */ -int usbi_get_config_index_by_value(struct libusb_device *dev, - uint8_t bConfigurationValue, int *idx) -{ - uint8_t i; - - usbi_dbg("value %d", bConfigurationValue); - for (i = 0; i < dev->num_configurations; i++) { - unsigned char tmp[6]; - int host_endian; - int r = usbi_backend.get_config_descriptor(dev, i, tmp, sizeof(tmp), - &host_endian); - if (r < 0) { - *idx = -1; - return r; - } - if (tmp[5] == bConfigurationValue) { - *idx = i; - return 0; - } - } - - *idx = -1; - return 0; -} - /** \ingroup libusb_desc * Get a USB configuration descriptor with a specific bConfigurationValue. * This is a non-blocking function which does not involve any requests being @@ -699,24 +642,33 @@ int usbi_get_config_index_by_value(struct libusb_device *dev, int API_EXPORTED libusb_get_config_descriptor_by_value(libusb_device *dev, uint8_t bConfigurationValue, struct libusb_config_descriptor **config) { - int r, idx, host_endian; - unsigned char *buf = NULL; + uint8_t idx; + int r; if (usbi_backend.get_config_descriptor_by_value) { + void *buf; + r = usbi_backend.get_config_descriptor_by_value(dev, - bConfigurationValue, &buf, &host_endian); + bConfigurationValue, &buf); if (r < 0) return r; - return raw_desc_to_config(dev->ctx, buf, r, host_endian, config); + + return raw_desc_to_config(DEVICE_CTX(dev), buf, r, config); } - r = usbi_get_config_index_by_value(dev, bConfigurationValue, &idx); - if (r < 0) - return r; - else if (idx == -1) - return LIBUSB_ERROR_NOT_FOUND; - else - return libusb_get_config_descriptor(dev, (uint8_t) idx, config); + usbi_dbg("value %u", bConfigurationValue); + for (idx = 0; idx < dev->device_descriptor.bNumConfigurations; idx++) { + union usbi_config_desc_buf _config; + + r = get_config_descriptor(dev, idx, _config.buf, sizeof(_config.buf)); + if (r < 0) + return r; + + if (_config.desc.bConfigurationValue == bConfigurationValue) + return libusb_get_config_descriptor(dev, idx, config); + } + + return LIBUSB_ERROR_NOT_FOUND; } /** \ingroup libusb_desc @@ -751,37 +703,41 @@ void API_EXPORTED libusb_free_config_descriptor( * \returns another LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_ss_endpoint_companion_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, const struct libusb_endpoint_descriptor *endpoint, struct libusb_ss_endpoint_companion_descriptor **ep_comp) { - struct usb_descriptor_header header; + struct usbi_descriptor_header *header; + const uint8_t *buffer = endpoint->extra; int size = endpoint->extra_length; - const unsigned char *buffer = endpoint->extra; *ep_comp = NULL; while (size >= DESC_HEADER_LENGTH) { - usbi_parse_descriptor(buffer, "bb", &header, 0); - if (header.bLength < 2 || header.bLength > size) { - usbi_err(ctx, "invalid descriptor length %d", - header.bLength); - return LIBUSB_ERROR_IO; - } - if (header.bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { - buffer += header.bLength; - size -= header.bLength; + header = (struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_SS_ENDPOINT_COMPANION) { + if (header->bLength < DESC_HEADER_LENGTH) { + usbi_err(ctx, "invalid descriptor length %u", + header->bLength); + return LIBUSB_ERROR_IO; + } + buffer += header->bLength; + size -= header->bLength; continue; - } - if (header.bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { - usbi_err(ctx, "invalid ss-ep-comp-desc length %d", - header.bLength); + } else if (header->bLength < LIBUSB_DT_SS_ENDPOINT_COMPANION_SIZE) { + usbi_err(ctx, "invalid ss-ep-comp-desc length %u", + header->bLength); + return LIBUSB_ERROR_IO; + } else if (header->bLength > size) { + usbi_err(ctx, "short ss-ep-comp-desc read %d/%u", + size, header->bLength); return LIBUSB_ERROR_IO; } + *ep_comp = malloc(sizeof(**ep_comp)); - if (*ep_comp == NULL) + if (!*ep_comp) return LIBUSB_ERROR_NO_MEM; - usbi_parse_descriptor(buffer, "bbbbw", *ep_comp, 0); + parse_descriptor(buffer, "bbbbw", *ep_comp); return LIBUSB_SUCCESS; } return LIBUSB_ERROR_NOT_FOUND; @@ -803,11 +759,12 @@ void API_EXPORTED libusb_free_ss_endpoint_companion_descriptor( static int parse_bos(struct libusb_context *ctx, struct libusb_bos_descriptor **bos, - unsigned char *buffer, int size, int host_endian) + const uint8_t *buffer, int size) { - struct libusb_bos_descriptor bos_header, *_bos; - struct libusb_bos_dev_capability_descriptor dev_cap; - int i; + struct libusb_bos_descriptor *_bos; + const struct usbi_bos_descriptor *bos_desc; + const struct usbi_descriptor_header *header; + uint8_t i; if (size < LIBUSB_DT_BOS_SIZE) { usbi_err(ctx, "short bos descriptor read %d/%d", @@ -815,66 +772,61 @@ static int parse_bos(struct libusb_context *ctx, return LIBUSB_ERROR_IO; } - usbi_parse_descriptor(buffer, "bbwb", &bos_header, host_endian); - if (bos_header.bDescriptorType != LIBUSB_DT_BOS) { - usbi_err(ctx, "unexpected descriptor %x (expected %x)", - bos_header.bDescriptorType, LIBUSB_DT_BOS); + bos_desc = (const struct usbi_bos_descriptor *)buffer; + if (bos_desc->bDescriptorType != LIBUSB_DT_BOS) { + usbi_err(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + bos_desc->bDescriptorType, LIBUSB_DT_BOS); return LIBUSB_ERROR_IO; - } - if (bos_header.bLength < LIBUSB_DT_BOS_SIZE) { - usbi_err(ctx, "invalid bos bLength (%d)", bos_header.bLength); + } else if (bos_desc->bLength < LIBUSB_DT_BOS_SIZE) { + usbi_err(ctx, "invalid bos bLength (%u)", bos_desc->bLength); return LIBUSB_ERROR_IO; - } - if (bos_header.bLength > size) { - usbi_err(ctx, "short bos descriptor read %d/%d", - size, bos_header.bLength); + } else if (bos_desc->bLength > size) { + usbi_err(ctx, "short bos descriptor read %d/%u", + size, bos_desc->bLength); return LIBUSB_ERROR_IO; } - _bos = calloc (1, - sizeof(*_bos) + bos_header.bNumDeviceCaps * sizeof(void *)); + _bos = calloc(1, sizeof(*_bos) + bos_desc->bNumDeviceCaps * sizeof(void *)); if (!_bos) return LIBUSB_ERROR_NO_MEM; - usbi_parse_descriptor(buffer, "bbwb", _bos, host_endian); - buffer += bos_header.bLength; - size -= bos_header.bLength; + parse_descriptor(buffer, "bbwb", _bos); + buffer += _bos->bLength; + size -= _bos->bLength; /* Get the device capability descriptors */ - for (i = 0; i < bos_header.bNumDeviceCaps; i++) { + for (i = 0; i < _bos->bNumDeviceCaps; i++) { if (size < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { usbi_warn(ctx, "short dev-cap descriptor read %d/%d", size, LIBUSB_DT_DEVICE_CAPABILITY_SIZE); break; } - usbi_parse_descriptor(buffer, "bbb", &dev_cap, host_endian); - if (dev_cap.bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { - usbi_warn(ctx, "unexpected descriptor %x (expected %x)", - dev_cap.bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); + header = (const struct usbi_descriptor_header *)buffer; + if (header->bDescriptorType != LIBUSB_DT_DEVICE_CAPABILITY) { + usbi_warn(ctx, "unexpected descriptor 0x%x (expected 0x%x)", + header->bDescriptorType, LIBUSB_DT_DEVICE_CAPABILITY); break; - } - if (dev_cap.bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { - usbi_err(ctx, "invalid dev-cap bLength (%d)", - dev_cap.bLength); + } else if (header->bLength < LIBUSB_DT_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "invalid dev-cap bLength (%u)", + header->bLength); libusb_free_bos_descriptor(_bos); return LIBUSB_ERROR_IO; - } - if (dev_cap.bLength > size) { - usbi_warn(ctx, "short dev-cap descriptor read %d/%d", - size, dev_cap.bLength); + } else if (header->bLength > size) { + usbi_warn(ctx, "short dev-cap descriptor read %d/%u", + size, header->bLength); break; } - _bos->dev_capability[i] = malloc(dev_cap.bLength); + _bos->dev_capability[i] = malloc(header->bLength); if (!_bos->dev_capability[i]) { libusb_free_bos_descriptor(_bos); return LIBUSB_ERROR_NO_MEM; } - memcpy(_bos->dev_capability[i], buffer, dev_cap.bLength); - buffer += dev_cap.bLength; - size -= dev_cap.bLength; + memcpy(_bos->dev_capability[i], buffer, header->bLength); + buffer += header->bLength; + size -= header->bLength; } - _bos->bNumDeviceCaps = (uint8_t)i; + _bos->bNumDeviceCaps = i; *bos = _bos; return LIBUSB_SUCCESS; @@ -894,16 +846,14 @@ static int parse_bos(struct libusb_context *ctx, int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, struct libusb_bos_descriptor **bos) { - struct libusb_bos_descriptor _bos; - uint8_t bos_header[LIBUSB_DT_BOS_SIZE] = {0}; - unsigned char *bos_data = NULL; - const int host_endian = 0; + union usbi_bos_desc_buf _bos; + uint16_t bos_len; + uint8_t *bos_data; int r; /* Read the BOS. This generates 2 requests on the bus, * one for the header, and one for the full BOS */ - r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_header, - LIBUSB_DT_BOS_SIZE); + r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, _bos.buf, sizeof(_bos.buf)); if (r < 0) { if (r != LIBUSB_ERROR_PIPE) usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); @@ -915,19 +865,22 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, return LIBUSB_ERROR_IO; } - usbi_parse_descriptor(bos_header, "bbwb", &_bos, host_endian); - usbi_dbg("found BOS descriptor: size %d bytes, %d capabilities", - _bos.wTotalLength, _bos.bNumDeviceCaps); - bos_data = calloc(_bos.wTotalLength, 1); - if (bos_data == NULL) + bos_len = libusb_le16_to_cpu(_bos.desc.wTotalLength); + usbi_dbg("found BOS descriptor: size %u bytes, %u capabilities", + bos_len, _bos.desc.bNumDeviceCaps); + bos_data = calloc(1, bos_len); + if (!bos_data) return LIBUSB_ERROR_NO_MEM; - r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, - _bos.wTotalLength); - if (r >= 0) - r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r, host_endian); - else + r = libusb_get_descriptor(dev_handle, LIBUSB_DT_BOS, 0, bos_data, bos_len); + if (r >= 0) { + if (r != (int)bos_len) + usbi_warn(HANDLE_CTX(dev_handle), "short BOS read %d/%u", + r, bos_len); + r = parse_bos(HANDLE_CTX(dev_handle), bos, bos_data, r); + } else { usbi_err(HANDLE_CTX(dev_handle), "failed to read BOS (%d)", r); + } free(bos_data); return r; @@ -942,7 +895,7 @@ int API_EXPORTED libusb_get_bos_descriptor(libusb_device_handle *dev_handle, */ void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) { - int i; + uint8_t i; if (!bos) return; @@ -966,21 +919,19 @@ void API_EXPORTED libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos) * \returns a LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension) { struct libusb_usb_2_0_extension_descriptor *_usb_2_0_extension; - const int host_endian = 0; if (dev_cap->bDevCapabilityType != LIBUSB_BT_USB_2_0_EXTENSION) { - usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", dev_cap->bDevCapabilityType, LIBUSB_BT_USB_2_0_EXTENSION); return LIBUSB_ERROR_INVALID_PARAM; - } - if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { - usbi_err(ctx, "short dev-cap descriptor read %d/%d", + } else if (dev_cap->bLength < LIBUSB_BT_USB_2_0_EXTENSION_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", dev_cap->bLength, LIBUSB_BT_USB_2_0_EXTENSION_SIZE); return LIBUSB_ERROR_IO; } @@ -989,8 +940,7 @@ int API_EXPORTED libusb_get_usb_2_0_extension_descriptor( if (!_usb_2_0_extension) return LIBUSB_ERROR_NO_MEM; - usbi_parse_descriptor((unsigned char *)dev_cap, "bbbd", - _usb_2_0_extension, host_endian); + parse_descriptor(dev_cap, "bbbd", _usb_2_0_extension); *usb_2_0_extension = _usb_2_0_extension; return LIBUSB_SUCCESS; @@ -1024,21 +974,19 @@ void API_EXPORTED libusb_free_usb_2_0_extension_descriptor( * \returns a LIBUSB_ERROR code on error */ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap) { struct libusb_ss_usb_device_capability_descriptor *_ss_usb_device_cap; - const int host_endian = 0; if (dev_cap->bDevCapabilityType != LIBUSB_BT_SS_USB_DEVICE_CAPABILITY) { - usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", dev_cap->bDevCapabilityType, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY); return LIBUSB_ERROR_INVALID_PARAM; - } - if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { - usbi_err(ctx, "short dev-cap descriptor read %d/%d", + } else if (dev_cap->bLength < LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", dev_cap->bLength, LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE); return LIBUSB_ERROR_IO; } @@ -1047,8 +995,7 @@ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( if (!_ss_usb_device_cap) return LIBUSB_ERROR_NO_MEM; - usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbwbbw", - _ss_usb_device_cap, host_endian); + parse_descriptor(dev_cap, "bbbbwbbw", _ss_usb_device_cap); *ss_usb_device_cap = _ss_usb_device_cap; return LIBUSB_SUCCESS; @@ -1060,7 +1007,8 @@ int API_EXPORTED libusb_get_ss_usb_device_capability_descriptor( * It is safe to call this function with a NULL ss_usb_device_cap * parameter, in which case the function simply returns. * - * \param ss_usb_device_cap the USB 2.0 Extension descriptor to free + * \param ss_usb_device_cap the SuperSpeed USB Device Capability descriptor + * to free */ void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap) @@ -1081,21 +1029,19 @@ void API_EXPORTED libusb_free_ss_usb_device_capability_descriptor( * \returns 0 on success * \returns a LIBUSB_ERROR code on error */ -int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx, +int API_EXPORTED libusb_get_container_id_descriptor(libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_container_id_descriptor **container_id) { struct libusb_container_id_descriptor *_container_id; - const int host_endian = 0; if (dev_cap->bDevCapabilityType != LIBUSB_BT_CONTAINER_ID) { - usbi_err(ctx, "unexpected bDevCapabilityType %x (expected %x)", + usbi_err(ctx, "unexpected bDevCapabilityType 0x%x (expected 0x%x)", dev_cap->bDevCapabilityType, LIBUSB_BT_CONTAINER_ID); return LIBUSB_ERROR_INVALID_PARAM; - } - if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { - usbi_err(ctx, "short dev-cap descriptor read %d/%d", + } else if (dev_cap->bLength < LIBUSB_BT_CONTAINER_ID_SIZE) { + usbi_err(ctx, "short dev-cap descriptor read %u/%d", dev_cap->bLength, LIBUSB_BT_CONTAINER_ID_SIZE); return LIBUSB_ERROR_IO; } @@ -1104,8 +1050,7 @@ int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx, if (!_container_id) return LIBUSB_ERROR_NO_MEM; - usbi_parse_descriptor((unsigned char *)dev_cap, "bbbbu", - _container_id, host_endian); + parse_descriptor(dev_cap, "bbbbu", _container_id); *container_id = _container_id; return LIBUSB_SUCCESS; @@ -1117,7 +1062,7 @@ int API_EXPORTED libusb_get_container_id_descriptor(struct libusb_context *ctx, * It is safe to call this function with a NULL container_id parameter, * in which case the function simply returns. * - * \param container_id the USB 2.0 Extension descriptor to free + * \param container_id the Container ID descriptor to free */ void API_EXPORTED libusb_free_container_id_descriptor( struct libusb_container_id_descriptor *container_id) @@ -1140,9 +1085,9 @@ void API_EXPORTED libusb_free_container_id_descriptor( int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_handle, uint8_t desc_index, unsigned char *data, int length) { - unsigned char tbuf[255]; /* Some devices choke on size > 255 */ + union usbi_string_desc_buf str; int r, si, di; - uint16_t langid; + uint16_t langid, wdata; /* Asking for the zero'th index is special - it returns a string * descriptor that contains all the language IDs supported by the @@ -1156,35 +1101,37 @@ int API_EXPORTED libusb_get_string_descriptor_ascii(libusb_device_handle *dev_ha if (desc_index == 0) return LIBUSB_ERROR_INVALID_PARAM; - r = libusb_get_string_descriptor(dev_handle, 0, 0, tbuf, sizeof(tbuf)); + r = libusb_get_string_descriptor(dev_handle, 0, 0, str.buf, 4); if (r < 0) return r; - - if (r < 4) + else if (r != 4 || str.desc.bLength < 4) return LIBUSB_ERROR_IO; + else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) + return LIBUSB_ERROR_IO; + else if (str.desc.bLength & 1) + usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength); - langid = tbuf[2] | (tbuf[3] << 8); - - r = libusb_get_string_descriptor(dev_handle, desc_index, langid, tbuf, - sizeof(tbuf)); + langid = libusb_le16_to_cpu(str.desc.wData[0]); + r = libusb_get_string_descriptor(dev_handle, desc_index, langid, str.buf, sizeof(str.buf)); if (r < 0) return r; - - if (tbuf[1] != LIBUSB_DT_STRING) + else if (r < DESC_HEADER_LENGTH || str.desc.bLength > r) return LIBUSB_ERROR_IO; - - if (tbuf[0] > r) + else if (str.desc.bDescriptorType != LIBUSB_DT_STRING) return LIBUSB_ERROR_IO; + else if ((str.desc.bLength & 1) || str.desc.bLength != r) + usbi_warn(HANDLE_CTX(dev_handle), "suspicious bLength %u for string descriptor", str.desc.bLength); di = 0; - for (si = 2; si < tbuf[0]; si += 2) { + for (si = 2; si < str.desc.bLength; si += 2) { if (di >= (length - 1)) break; - if ((tbuf[si] & 0x80) || (tbuf[si + 1])) /* non-ASCII */ - data[di++] = '?'; + wdata = libusb_le16_to_cpu(str.desc.wData[di]); + if (wdata < 0x80) + data[di++] = (unsigned char)wdata; else - data[di++] = tbuf[si]; + data[di++] = '?'; /* non-ASCII */ } data[di] = 0; diff --git a/mac/libusb/hotplug.c b/mac/libusb/hotplug.c index a4320bc42..e3e5e76e6 100644 --- a/mac/libusb/hotplug.c +++ b/mac/libusb/hotplug.c @@ -19,17 +19,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - -#include -#include -#include -#include -#ifdef HAVE_SYS_TYPES_H -#include -#endif -#include - #include "libusbi.h" #include "hotplug.h" @@ -48,7 +37,7 @@ * Version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102, has added support * for hotplug events on some platforms (you should test if your platform * supports hotplug notification by calling \ref libusb_has_capability() with - * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). + * parameter \ref LIBUSB_CAP_HAS_HOTPLUG). * * This interface allows you to request notification for the arrival and departure * of matching USB devices. @@ -61,8 +50,8 @@ * expecting additional events. Returning 0 will rearm the callback and 1 will cause * the callback to be deregistered. Note that when callbacks are called from * libusb_hotplug_register_callback() because of the \ref LIBUSB_HOTPLUG_ENUMERATE - * flag, the callback return value is ignored, iow you cannot cause a callback - * to be deregistered by returning 1 when it is called from + * flag, the callback return value is ignored. In other words, you cannot cause a + * callback to be deregistered by returning 1 when it is called from * libusb_hotplug_register_callback(). * * Callbacks for a particular context are automatically deregistered by libusb_exit(). @@ -154,6 +143,13 @@ int main (void) { \endcode */ +#define VALID_HOTPLUG_EVENTS \ + (LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | \ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + +#define VALID_HOTPLUG_FLAGS \ + (LIBUSB_HOTPLUG_ENUMERATE) + static int usbi_hotplug_match_cb(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event, struct libusb_hotplug_callback *hotplug_cb) @@ -188,7 +184,7 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, usbi_mutex_lock(&ctx->hotplug_cbs_lock); - list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { + for_each_hotplug_cb_safe(ctx, hotplug_cb, next) { if (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE) { /* process deregistration in usbi_hotplug_deregister() */ continue; @@ -210,8 +206,8 @@ void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, libusb_hotplug_event event) { - int pending_events; struct libusb_hotplug_message *message = calloc(1, sizeof(*message)); + unsigned int event_flags; if (!message) { usbi_err(ctx, "error allocating hotplug message"); @@ -224,15 +220,16 @@ void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device /* Take the event data lock and add this message to the list. * Only signal an event if there are no prior pending events. */ usbi_mutex_lock(&ctx->event_data_lock); - pending_events = usbi_pending_events(ctx); + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_HOTPLUG_MSG_PENDING; list_add_tail(&message->list, &ctx->hotplug_msgs); - if (!pending_events) - usbi_signal_event(ctx); + if (!event_flags) + usbi_signal_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); } int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, - libusb_hotplug_event events, libusb_hotplug_flag flags, + int events, int flags, int vendor_id, int product_id, int dev_class, libusb_hotplug_callback_fn cb_fn, void *user_data, libusb_hotplug_callback_handle *callback_handle) @@ -240,8 +237,8 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, struct libusb_hotplug_callback *new_callback; /* check for sane values */ - if ((!events || (~(LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED | LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) & events)) || - (flags && (~LIBUSB_HOTPLUG_ENUMERATE & flags)) || + if ((!events || (~VALID_HOTPLUG_EVENTS & events)) || + (~VALID_HOTPLUG_FLAGS & flags) || (LIBUSB_HOTPLUG_MATCH_ANY != vendor_id && (~0xffff & vendor_id)) || (LIBUSB_HOTPLUG_MATCH_ANY != product_id && (~0xffff & product_id)) || (LIBUSB_HOTPLUG_MATCH_ANY != dev_class && (~0xff & dev_class)) || @@ -254,7 +251,7 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, return LIBUSB_ERROR_NOT_SUPPORTED; } - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); new_callback = calloc(1, sizeof(*new_callback)); if (!new_callback) { @@ -319,7 +316,7 @@ int API_EXPORTED libusb_hotplug_register_callback(libusb_context *ctx, return LIBUSB_SUCCESS; } -void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx, +void API_EXPORTED libusb_hotplug_deregister_callback(libusb_context *ctx, libusb_hotplug_callback_handle callback_handle) { struct libusb_hotplug_callback *hotplug_cb; @@ -330,12 +327,12 @@ void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx, return; } - USBI_GET_CONTEXT(ctx); - usbi_dbg("deregister hotplug cb %d", callback_handle); + ctx = usbi_get_context(ctx); + usbi_mutex_lock(&ctx->hotplug_cbs_lock); - list_for_each_entry(hotplug_cb, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { + for_each_hotplug_cb(ctx, hotplug_cb) { if (callback_handle == hotplug_cb->handle) { /* Mark this callback for deregistration */ hotplug_cb->flags |= USBI_HOTPLUG_NEEDS_FREE; @@ -345,23 +342,50 @@ void API_EXPORTED libusb_hotplug_deregister_callback(struct libusb_context *ctx, usbi_mutex_unlock(&ctx->hotplug_cbs_lock); if (deregistered) { - int pending_events; + unsigned int event_flags; usbi_mutex_lock(&ctx->event_data_lock); - pending_events = usbi_pending_events(ctx); + event_flags = ctx->event_flags; ctx->event_flags |= USBI_EVENT_HOTPLUG_CB_DEREGISTERED; - if (!pending_events) - usbi_signal_event(ctx); + if (!event_flags) + usbi_signal_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); } } +DEFAULT_VISIBILITY +void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle) +{ + struct libusb_hotplug_callback *hotplug_cb; + void *user_data = NULL; + + /* check for hotplug support */ + if (!libusb_has_capability(LIBUSB_CAP_HAS_HOTPLUG)) { + return NULL; + } + + usbi_dbg("get hotplug user data %d", callback_handle); + + ctx = usbi_get_context(ctx); + + usbi_mutex_lock(&ctx->hotplug_cbs_lock); + for_each_hotplug_cb(ctx, hotplug_cb) { + if (callback_handle == hotplug_cb->handle) { + user_data = hotplug_cb->user_data; + } + } + usbi_mutex_unlock(&ctx->hotplug_cbs_lock); + + return user_data; +} + void usbi_hotplug_deregister(struct libusb_context *ctx, int forced) { struct libusb_hotplug_callback *hotplug_cb, *next; usbi_mutex_lock(&ctx->hotplug_cbs_lock); - list_for_each_entry_safe(hotplug_cb, next, &ctx->hotplug_cbs, list, struct libusb_hotplug_callback) { + for_each_hotplug_cb_safe(ctx, hotplug_cb, next) { if (forced || (hotplug_cb->flags & USBI_HOTPLUG_NEEDS_FREE)) { usbi_dbg("freeing hotplug cb %p with handle %d", hotplug_cb, hotplug_cb->handle); diff --git a/mac/libusb/hotplug.h b/mac/libusb/hotplug.h index dbadbcb93..161f7e5fc 100644 --- a/mac/libusb/hotplug.h +++ b/mac/libusb/hotplug.h @@ -36,16 +36,16 @@ enum usbi_hotplug_flags { */ /* The vendor_id field is valid for matching */ - USBI_HOTPLUG_VENDOR_ID_VALID = (1 << 3), + USBI_HOTPLUG_VENDOR_ID_VALID = (1U << 3), /* The product_id field is valid for matching */ - USBI_HOTPLUG_PRODUCT_ID_VALID = (1 << 4), + USBI_HOTPLUG_PRODUCT_ID_VALID = (1U << 4), /* The dev_class field is valid for matching */ - USBI_HOTPLUG_DEV_CLASS_VALID = (1 << 5), + USBI_HOTPLUG_DEV_CLASS_VALID = (1U << 5), /* This callback has been unregistered and needs to be freed */ - USBI_HOTPLUG_NEEDS_FREE = (1 << 6), + USBI_HOTPLUG_NEEDS_FREE = (1U << 6), }; /** \ingroup hotplug @@ -90,10 +90,16 @@ struct libusb_hotplug_message { struct list_head list; }; +#define for_each_hotplug_cb(ctx, c) \ + for_each_helper(c, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) + +#define for_each_hotplug_cb_safe(ctx, c, n) \ + for_each_safe_helper(c, n, &(ctx)->hotplug_cbs, struct libusb_hotplug_callback) + void usbi_hotplug_deregister(struct libusb_context *ctx, int forced); void usbi_hotplug_match(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); + libusb_hotplug_event event); void usbi_hotplug_notification(struct libusb_context *ctx, struct libusb_device *dev, - libusb_hotplug_event event); + libusb_hotplug_event event); #endif diff --git a/mac/libusb/io.c b/mac/libusb/io.c index a03bfaae1..0e960ddfd 100644 --- a/mac/libusb/io.c +++ b/mac/libusb/io.c @@ -3,6 +3,8 @@ * I/O functions for libusb * Copyright © 2007-2009 Daniel Drake * Copyright © 2001 Johannes Erdfelt + * Copyright © 2019 Nathan Hjelm + * Copyright © 2019 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -19,21 +21,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - -#include -#include -#include -#include -#include -#include -#ifdef HAVE_SYS_TIME_H -#include -#endif -#ifdef USBI_TIMERFD_AVAILABLE -#include -#endif - #include "libusbi.h" #include "hotplug.h" @@ -86,7 +73,7 @@ * a single function call. When the function call returns, the transfer has * completed and you can parse the results. * - * If you have used the libusb-0.1 before, this I/O style will seem familar to + * If you have used the libusb-0.1 before, this I/O style will seem familiar to * you. libusb-0.1 only offered a synchronous interface. * * In our input device example, to read button presses you might write code @@ -112,7 +99,7 @@ if (r == 0 && actual_length == sizeof(data)) { * sleeping for that long. Execution will be tied up inside the library - * the entire thread will be useless for that duration. * - * Another issue is that by tieing up the thread with that single transaction + * Another issue is that by tying up the thread with that single transaction * there is no possibility of performing I/O with multiple endpoints and/or * multiple devices simultaneously, unless you resort to creating one thread * per transaction. @@ -330,9 +317,40 @@ if (r == 0 && actual_length == sizeof(data)) { * Freeing the transfer after it has been cancelled but before cancellation * has completed will result in undefined behaviour. * + * \attention * When a transfer is cancelled, some of the data may have been transferred. - * libusb will communicate this to you in the transfer callback. Do not assume - * that no data was transferred. + * libusb will communicate this to you in the transfer callback. + * Do not assume that no data was transferred. + * + * \section asyncpartial Partial data transfer resulting from cancellation + * + * As noted above, some of the data may have been transferred at the time a + * transfer is cancelled. It is helpful to see how this is possible if you + * consider a bulk transfer to an endpoint with a packet size of 64 bytes. + * Supposing you submit a 512-byte transfer to this endpoint, the operating + * system will divide this transfer up into 8 separate 64-byte frames that the + * host controller will schedule for the device to transfer data. If this + * transfer is cancelled while the device is transferring data, a subset of + * these frames may be descheduled from the host controller before the device + * has the opportunity to finish transferring data to the host. + * + * What your application should do with a partial data transfer is a policy + * decision; there is no single answer that satisfies the needs of every + * application. The data that was successfully transferred should be + * considered entirely valid, but your application must decide what to do with + * the remaining data that was not transferred. Some possible actions to take + * are: + * - Resubmit another transfer for the remaining data, possibly with a shorter + * timeout + * - Discard the partially transferred data and report an error + * + * \section asynctimeout Timeouts + * + * When a transfer times out, libusb internally notes this and attempts to + * cancel the transfer. As noted in \ref asyncpartial "above", it is possible + * that some of the data may actually have been transferred. Your application + * should always check how much data was actually transferred once the + * transfer completes and act accordingly. * * \section bulk_overflows Overflows on device-to-host bulk/interrupt endpoints * @@ -390,7 +408,7 @@ if (r == 0 && actual_length == sizeof(data)) { * wLength of the setup packet, rather than the size of the data buffer. So, * if your wLength was 4, your transfer's length was 12, then you * should expect an actual_length of 4 to indicate that the data was - * transferred in entirity. + * transferred in entirety. * * To simplify parsing of setup packets and obtaining the data from the * correct offset, you may wish to use the libusb_control_transfer_get_data() @@ -469,14 +487,21 @@ if (r == 0 && actual_length == sizeof(data)) { * libusb_get_iso_packet_buffer() and libusb_get_iso_packet_buffer_simple() * functions may help you here. * - * Note: Some operating systems (e.g. Linux) may impose limits on the - * length of individual isochronous packets and/or the total length of the - * isochronous transfer. Such limits can be difficult for libusb to detect, - * so the library will simply try and submit the transfer as set up by you. - * If the transfer fails to submit because it is too large, + * \section asynclimits Transfer length limitations + * + * Some operating systems may impose limits on the length of the transfer data + * buffer or, in the case of isochronous transfers, the length of individual + * isochronous packets. Such limits can be difficult for libusb to detect, so + * in most cases the library will simply try and submit the transfer as set up + * by you. If the transfer fails to submit because it is too large, * libusb_submit_transfer() will return * \ref libusb_error::LIBUSB_ERROR_INVALID_PARAM "LIBUSB_ERROR_INVALID_PARAM". * + * The following are known limits for control transfer lengths. Note that this + * length includes the 8-byte setup packet. + * - Linux (4,096 bytes) + * - Windows (4,096 bytes) + * * \section asyncmem Memory caveats * * In most circumstances, it is not safe to use stack memory for transfer @@ -517,7 +542,14 @@ if (r == 0 && actual_length == sizeof(data)) { * application must call into when libusb has work do to. This gives libusb * the opportunity to reap pending transfers, invoke callbacks, etc. * - * There are 2 different approaches to dealing with libusb_handle_events: + * \note + * All event handling is performed by whichever thread calls the + * libusb_handle_events() function. libusb does not invoke any callbacks + * outside of this context. Consequently, any callbacks will be run on the + * thread that calls the libusb_handle_events() function. + * + * When to call the libusb_handle_events() function depends on which model + * your application decides to use. The 2 different approaches: * * -# Repeatedly call libusb_handle_events() in blocking mode from a dedicated * thread. @@ -538,7 +570,7 @@ if (r == 0 && actual_length == sizeof(data)) { * * Lets begin with stating the obvious: If you're going to use a separate * thread for libusb event handling, your callback functions MUST be - * threadsafe. + * thread-safe. * * Other then that doing event handling from a separate thread, is mostly * simple. You can use an event thread function as follows: @@ -778,7 +810,7 @@ while (user has not requested application exit) { * system calls. This is directly exposed at the * \ref libusb_asyncio "asynchronous interface" but it is important to note that the * \ref libusb_syncio "synchronous interface" is implemented on top of the - * asynchonrous interface, therefore the same considerations apply. + * asynchronous interface, therefore the same considerations apply. * * The issue is that if two or more threads are concurrently calling poll() * or select() on libusb's file descriptors then only one of those threads @@ -886,6 +918,11 @@ void myfunc() { * do is submit a single transfer and wait for its completion, then using * one of the synchronous I/O functions is much easier. * + * \note + * The `completed` variable must be modified while holding the event lock, + * otherwise a race condition can still exist. It is simplest to do so from + * within the transfer callback as shown above. + * * \section eventlock The events lock * * The problem is when we consider the fact that libusb exposes file @@ -983,7 +1020,7 @@ printf("completed!\n"); * event handling), because the event waiter seems to have taken the event * waiters lock while waiting for an event. However, the system does support * multiple event waiters, because libusb_wait_for_event() actually drops - * the lock while waiting, and reaquires it before continuing. + * the lock while waiting, and reacquires it before continuing. * * We have now implemented code which can dynamically handle situations where * nobody is handling events (so we should do it ourselves), and it can also @@ -1128,45 +1165,40 @@ int usbi_io_init(struct libusb_context *ctx) usbi_mutex_init(&ctx->event_data_lock); usbi_tls_key_create(&ctx->event_handling_key); list_init(&ctx->flying_transfers); - list_init(&ctx->ipollfds); + list_init(&ctx->event_sources); + list_init(&ctx->removed_event_sources); list_init(&ctx->hotplug_msgs); list_init(&ctx->completed_transfers); - /* FIXME should use an eventfd on kernels that support it */ - r = usbi_pipe(ctx->event_pipe); - if (r < 0) { - r = LIBUSB_ERROR_OTHER; + r = usbi_create_event(&ctx->event); + if (r < 0) goto err; - } - r = usbi_add_pollfd(ctx, ctx->event_pipe[0], POLLIN); + r = usbi_add_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event), USBI_EVENT_POLL_EVENTS); if (r < 0) - goto err_close_pipe; - -#ifdef USBI_TIMERFD_AVAILABLE - ctx->timerfd = timerfd_create(usbi_backend.get_timerfd_clockid(), - TFD_NONBLOCK | TFD_CLOEXEC); - if (ctx->timerfd >= 0) { - usbi_dbg("using timerfd for timeouts"); - r = usbi_add_pollfd(ctx, ctx->timerfd, POLLIN); + goto err_destroy_event; + +#ifdef HAVE_OS_TIMER + r = usbi_create_timer(&ctx->timer); + if (r == 0) { + usbi_dbg("using timer for timeouts"); + r = usbi_add_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer), USBI_TIMER_POLL_EVENTS); if (r < 0) - goto err_close_timerfd; + goto err_destroy_timer; } else { - usbi_dbg("timerfd not available (code %d error %d)", ctx->timerfd, errno); - ctx->timerfd = -1; + usbi_dbg("timer not available for timeouts"); } #endif return 0; -#ifdef USBI_TIMERFD_AVAILABLE -err_close_timerfd: - close(ctx->timerfd); - usbi_remove_pollfd(ctx, ctx->event_pipe[0]); +#ifdef HAVE_OS_TIMER +err_destroy_timer: + usbi_destroy_timer(&ctx->timer); + usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event)); #endif -err_close_pipe: - usbi_close(ctx->event_pipe[0]); - usbi_close(ctx->event_pipe[1]); +err_destroy_event: + usbi_destroy_event(&ctx->event); err: usbi_mutex_destroy(&ctx->flying_transfers_lock); usbi_mutex_destroy(&ctx->events_lock); @@ -1177,56 +1209,54 @@ err: return r; } +static void cleanup_removed_event_sources(struct libusb_context *ctx) +{ + struct usbi_event_source *ievent_source, *tmp; + + for_each_removed_event_source_safe(ctx, ievent_source, tmp) { + list_del(&ievent_source->list); + free(ievent_source); + } +} + void usbi_io_exit(struct libusb_context *ctx) { - usbi_remove_pollfd(ctx, ctx->event_pipe[0]); - usbi_close(ctx->event_pipe[0]); - usbi_close(ctx->event_pipe[1]); -#ifdef USBI_TIMERFD_AVAILABLE - if (usbi_using_timerfd(ctx)) { - usbi_remove_pollfd(ctx, ctx->timerfd); - close(ctx->timerfd); +#ifdef HAVE_OS_TIMER + if (usbi_using_timer(ctx)) { + usbi_remove_event_source(ctx, USBI_TIMER_OS_HANDLE(&ctx->timer)); + usbi_destroy_timer(&ctx->timer); } #endif + usbi_remove_event_source(ctx, USBI_EVENT_OS_HANDLE(&ctx->event)); + usbi_destroy_event(&ctx->event); usbi_mutex_destroy(&ctx->flying_transfers_lock); usbi_mutex_destroy(&ctx->events_lock); usbi_mutex_destroy(&ctx->event_waiters_lock); usbi_cond_destroy(&ctx->event_waiters_cond); usbi_mutex_destroy(&ctx->event_data_lock); usbi_tls_key_delete(ctx->event_handling_key); - if (ctx->pollfds) - free(ctx->pollfds); + cleanup_removed_event_sources(ctx); + free(ctx->event_data); } -static int calculate_timeout(struct usbi_transfer *transfer) +static void calculate_timeout(struct usbi_transfer *itransfer) { - int r; - struct timespec current_time; unsigned int timeout = - USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout; + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout; if (!timeout) { - timerclear(&transfer->timeout); - return 0; - } - - r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, ¤t_time); - if (r < 0) { - usbi_err(ITRANSFER_CTX(transfer), - "failed to read monotonic clock, errno=%d", errno); - return r; + TIMESPEC_CLEAR(&itransfer->timeout); + return; } - current_time.tv_sec += timeout / 1000; - current_time.tv_nsec += (timeout % 1000) * 1000000; + usbi_get_monotonic_time(&itransfer->timeout); - while (current_time.tv_nsec >= 1000000000) { - current_time.tv_nsec -= 1000000000; - current_time.tv_sec++; + itransfer->timeout.tv_sec += timeout / 1000U; + itransfer->timeout.tv_nsec += (timeout % 1000U) * 1000000L; + if (itransfer->timeout.tv_nsec >= NSEC_PER_SEC) { + ++itransfer->timeout.tv_sec; + itransfer->timeout.tv_nsec -= NSEC_PER_SEC; } - - TIMESPEC_TO_TIMEVAL(&transfer->timeout, ¤t_time); - return 0; } /** \ingroup libusb_asyncio @@ -1249,24 +1279,35 @@ static int calculate_timeout(struct usbi_transfer *transfer) * use it on a non-isochronous endpoint. If you do this, ensure that at time * of submission, num_iso_packets is 0 and that type is set appropriately. * - * \param iso_packets number of isochronous packet descriptors to allocate + * \param iso_packets number of isochronous packet descriptors to allocate. Must be non-negative. * \returns a newly allocated transfer, or NULL on error */ DEFAULT_VISIBILITY struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( int iso_packets) { + size_t priv_size; + size_t alloc_size; + unsigned char *ptr; + struct usbi_transfer *itransfer; struct libusb_transfer *transfer; - size_t os_alloc_size = usbi_backend.transfer_priv_size; - size_t alloc_size = sizeof(struct usbi_transfer) + + assert(iso_packets >= 0); + if (iso_packets < 0) + return NULL; + + priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + alloc_size = priv_size + + sizeof(struct usbi_transfer) + sizeof(struct libusb_transfer) - + (sizeof(struct libusb_iso_packet_descriptor) * iso_packets) - + os_alloc_size; - struct usbi_transfer *itransfer = calloc(1, alloc_size); - if (!itransfer) + + (sizeof(struct libusb_iso_packet_descriptor) * (size_t)iso_packets); + ptr = calloc(1, alloc_size); + if (!ptr) return NULL; + itransfer = (struct usbi_transfer *)(ptr + priv_size); itransfer->num_iso_packets = iso_packets; + itransfer->priv = ptr; usbi_mutex_init(&itransfer->lock); transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); usbi_dbg("transfer %p", transfer); @@ -1293,67 +1334,58 @@ struct libusb_transfer * LIBUSB_CALL libusb_alloc_transfer( void API_EXPORTED libusb_free_transfer(struct libusb_transfer *transfer) { struct usbi_transfer *itransfer; + size_t priv_size; + unsigned char *ptr; + if (!transfer) return; usbi_dbg("transfer %p", transfer); - if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER && transfer->buffer) + if (transfer->flags & LIBUSB_TRANSFER_FREE_BUFFER) free(transfer->buffer); itransfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer); usbi_mutex_destroy(&itransfer->lock); - free(itransfer); -} -#ifdef USBI_TIMERFD_AVAILABLE -static int disarm_timerfd(struct libusb_context *ctx) -{ - const struct itimerspec disarm_timer = { { 0, 0 }, { 0, 0 } }; - int r; - - usbi_dbg(""); - r = timerfd_settime(ctx->timerfd, 0, &disarm_timer, NULL); - if (r < 0) - return LIBUSB_ERROR_OTHER; - else - return 0; + priv_size = PTR_ALIGN(usbi_backend.transfer_priv_size); + ptr = (unsigned char *)itransfer - priv_size; + assert(ptr == itransfer->priv); + free(ptr); } -/* iterates through the flying transfers, and rearms the timerfd based on the +/* iterates through the flying transfers, and rearms the timer based on the * next upcoming timeout. * must be called with flying_list locked. * returns 0 on success or a LIBUSB_ERROR code on failure. */ -static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) +#ifdef HAVE_OS_TIMER +static int arm_timer_for_next_timeout(struct libusb_context *ctx) { - struct usbi_transfer *transfer; + struct usbi_transfer *itransfer; + + if (!usbi_using_timer(ctx)) + return 0; - list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { - struct timeval *cur_tv = &transfer->timeout; + for_each_transfer(ctx, itransfer) { + struct timespec *cur_ts = &itransfer->timeout; /* if we've reached transfers of infinite timeout, then we have no * arming to do */ - if (!timerisset(cur_tv)) - goto disarm; + if (!TIMESPEC_IS_SET(cur_ts)) + break; /* act on first transfer that has not already been handled */ - if (!(transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { - int r; - const struct itimerspec it = { {0, 0}, - { cur_tv->tv_sec, cur_tv->tv_usec * 1000 } }; - usbi_dbg("next timeout originally %dms", USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout); - r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL); - if (r < 0) - return LIBUSB_ERROR_OTHER; - return 0; + if (!(itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT))) { + usbi_dbg("next timeout originally %ums", USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + return usbi_arm_timer(&ctx->timer, cur_ts); } } -disarm: - return disarm_timerfd(ctx); + usbi_dbg("no timeouts, disarming timer"); + return usbi_disarm_timer(&ctx->timer); } #else -static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) +static inline int arm_timer_for_next_timeout(struct libusb_context *ctx) { UNUSED(ctx); return 0; @@ -1363,40 +1395,36 @@ static int arm_timerfd_for_next_timeout(struct libusb_context *ctx) /* add a transfer to the (timeout-sorted) active transfers list. * This function will return non 0 if fails to update the timer, * in which case the transfer is *not* on the flying_transfers list. */ -static int add_to_flying_list(struct usbi_transfer *transfer) +static int add_to_flying_list(struct usbi_transfer *itransfer) { struct usbi_transfer *cur; - struct timeval *timeout = &transfer->timeout; - struct libusb_context *ctx = ITRANSFER_CTX(transfer); - int r; + struct timespec *timeout = &itransfer->timeout; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int r = 0; int first = 1; - r = calculate_timeout(transfer); - if (r) - return r; + calculate_timeout(itransfer); /* if we have no other flying transfers, start the list with this one */ if (list_empty(&ctx->flying_transfers)) { - list_add(&transfer->list, &ctx->flying_transfers); + list_add(&itransfer->list, &ctx->flying_transfers); goto out; } /* if we have infinite timeout, append to end of list */ - if (!timerisset(timeout)) { - list_add_tail(&transfer->list, &ctx->flying_transfers); + if (!TIMESPEC_IS_SET(timeout)) { + list_add_tail(&itransfer->list, &ctx->flying_transfers); /* first is irrelevant in this case */ goto out; } /* otherwise, find appropriate place in list */ - list_for_each_entry(cur, &ctx->flying_transfers, list, struct usbi_transfer) { + for_each_transfer(ctx, cur) { /* find first timeout that occurs after the transfer in question */ - struct timeval *cur_tv = &cur->timeout; + struct timespec *cur_ts = &cur->timeout; - if (!timerisset(cur_tv) || (cur_tv->tv_sec > timeout->tv_sec) || - (cur_tv->tv_sec == timeout->tv_sec && - cur_tv->tv_usec > timeout->tv_usec)) { - list_add_tail(&transfer->list, &cur->list); + if (!TIMESPEC_IS_SET(cur_ts) || TIMESPEC_CMP(cur_ts, timeout, >)) { + list_add_tail(&itransfer->list, &cur->list); goto out; } first = 0; @@ -1404,28 +1432,22 @@ static int add_to_flying_list(struct usbi_transfer *transfer) /* first is 0 at this stage (list not empty) */ /* otherwise we need to be inserted at the end */ - list_add_tail(&transfer->list, &ctx->flying_transfers); + list_add_tail(&itransfer->list, &ctx->flying_transfers); out: -#ifdef USBI_TIMERFD_AVAILABLE - if (first && usbi_using_timerfd(ctx) && timerisset(timeout)) { +#ifdef HAVE_OS_TIMER + if (first && usbi_using_timer(ctx) && TIMESPEC_IS_SET(timeout)) { /* if this transfer has the lowest timeout of all active transfers, - * rearm the timerfd with this transfer's timeout */ - const struct itimerspec it = { {0, 0}, - { timeout->tv_sec, timeout->tv_usec * 1000 } }; - usbi_dbg("arm timerfd for timeout in %dms (first in line)", - USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer)->timeout); - r = timerfd_settime(ctx->timerfd, TFD_TIMER_ABSTIME, &it, NULL); - if (r < 0) { - usbi_warn(ctx, "failed to arm first timerfd (errno %d)", errno); - r = LIBUSB_ERROR_OTHER; - } + * rearm the timer with this transfer's timeout */ + usbi_dbg("arm timer for timeout in %ums (first in line)", + USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->timeout); + r = usbi_arm_timer(&ctx->timer, timeout); } #else UNUSED(first); #endif if (r) - list_del(&transfer->list); + list_del(&itransfer->list); return r; } @@ -1434,18 +1456,18 @@ out: * This function will *always* remove the transfer from the * flying_transfers list. It will return a LIBUSB_ERROR code * if it fails to update the timer for the next timeout. */ -static int remove_from_flying_list(struct usbi_transfer *transfer) +static int remove_from_flying_list(struct usbi_transfer *itransfer) { - struct libusb_context *ctx = ITRANSFER_CTX(transfer); - int rearm_timerfd; + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); + int rearm_timer; int r = 0; usbi_mutex_lock(&ctx->flying_transfers_lock); - rearm_timerfd = (timerisset(&transfer->timeout) && - list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == transfer); - list_del(&transfer->list); - if (usbi_using_timerfd(ctx) && rearm_timerfd) - r = arm_timerfd_for_next_timeout(ctx); + rearm_timer = (TIMESPEC_IS_SET(&itransfer->timeout) && + list_first_entry(&ctx->flying_transfers, struct usbi_transfer, list) == itransfer); + list_del(&itransfer->list); + if (rearm_timer) + r = arm_timer_for_next_timeout(ctx); usbi_mutex_unlock(&ctx->flying_transfers_lock); return r; @@ -1462,7 +1484,7 @@ static int remove_from_flying_list(struct usbi_transfer *transfer) * \returns LIBUSB_ERROR_NOT_SUPPORTED if the transfer flags are not supported * by the operating system. * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than - * the operating system and/or hardware can support + * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failure */ int API_EXPORTED libusb_submit_transfer(struct libusb_transfer *transfer) @@ -1645,7 +1667,7 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, r = remove_from_flying_list(itransfer); if (r < 0) - usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout, errno=%d", errno); + usbi_err(ITRANSFER_CTX(itransfer), "failed to set timer for next timeout"); usbi_mutex_lock(&itransfer->lock); itransfer->state_flags &= ~USBI_TRANSFER_IN_FLIGHT; @@ -1682,39 +1704,44 @@ int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, * Do not call this function with the usbi_transfer lock held. User-specified * callback functions may attempt to directly resubmit the transfer, which * will attempt to take the lock. */ -int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer) +int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer) { - struct libusb_context *ctx = ITRANSFER_CTX(transfer); + struct libusb_context *ctx = ITRANSFER_CTX(itransfer); uint8_t timed_out; usbi_mutex_lock(&ctx->flying_transfers_lock); - timed_out = transfer->timeout_flags & USBI_TRANSFER_TIMED_OUT; + timed_out = itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT; usbi_mutex_unlock(&ctx->flying_transfers_lock); /* if the URB was cancelled due to timeout, report timeout to the user */ if (timed_out) { usbi_dbg("detected timeout cancellation"); - return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_TIMED_OUT); + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_TIMED_OUT); } /* otherwise its a normal async cancel */ - return usbi_handle_transfer_completion(transfer, LIBUSB_TRANSFER_CANCELLED); + return usbi_handle_transfer_completion(itransfer, LIBUSB_TRANSFER_CANCELLED); } /* Add a completed transfer to the completed_transfers list of the * context and signal the event. The backend's handle_transfer_completion() * function will be called the next time an event handler runs. */ -void usbi_signal_transfer_completion(struct usbi_transfer *transfer) +void usbi_signal_transfer_completion(struct usbi_transfer *itransfer) { - struct libusb_context *ctx = ITRANSFER_CTX(transfer); - int pending_events; + libusb_device_handle *dev_handle = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer)->dev_handle; - usbi_mutex_lock(&ctx->event_data_lock); - pending_events = usbi_pending_events(ctx); - list_add_tail(&transfer->completed_list, &ctx->completed_transfers); - if (!pending_events) - usbi_signal_event(ctx); - usbi_mutex_unlock(&ctx->event_data_lock); + if (dev_handle) { + struct libusb_context *ctx = HANDLE_CTX(dev_handle); + unsigned int event_flags; + + usbi_mutex_lock(&ctx->event_data_lock); + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_TRANSFER_COMPLETED; + list_add_tail(&itransfer->completed_list, &ctx->completed_transfers); + if (!event_flags) + usbi_signal_event(&ctx->event); + usbi_mutex_unlock(&ctx->event_data_lock); + } } /** \ingroup libusb_poll @@ -1740,7 +1767,8 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) { int r; unsigned int ru; - USBI_GET_CONTEXT(ctx); + + ctx = usbi_get_context(ctx); /* is someone else waiting to close a device? if so, don't let this thread * start event handling */ @@ -1753,7 +1781,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) } r = usbi_mutex_trylock(&ctx->events_lock); - if (r) + if (!r) return 1; ctx->event_handler_active = 1; @@ -1780,7 +1808,7 @@ int API_EXPORTED libusb_try_lock_events(libusb_context *ctx) */ void API_EXPORTED libusb_lock_events(libusb_context *ctx) { - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->events_lock); ctx->event_handler_active = 1; } @@ -1795,7 +1823,7 @@ void API_EXPORTED libusb_lock_events(libusb_context *ctx) */ void API_EXPORTED libusb_unlock_events(libusb_context *ctx) { - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); ctx->event_handler_active = 0; usbi_mutex_unlock(&ctx->events_lock); @@ -1831,7 +1859,8 @@ void API_EXPORTED libusb_unlock_events(libusb_context *ctx) int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) { unsigned int r; - USBI_GET_CONTEXT(ctx); + + ctx = usbi_get_context(ctx); /* is someone else waiting to close a device? if so, don't let this thread * continue event handling */ @@ -1859,7 +1888,8 @@ int API_EXPORTED libusb_event_handling_ok(libusb_context *ctx) int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) { unsigned int r; - USBI_GET_CONTEXT(ctx); + + ctx = usbi_get_context(ctx); /* is someone else waiting to close a device? if so, don't let this thread * start event handling -- indicate that event handling is happening */ @@ -1886,16 +1916,17 @@ int API_EXPORTED libusb_event_handler_active(libusb_context *ctx) */ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) { - int pending_events; - USBI_GET_CONTEXT(ctx); + unsigned int event_flags; - usbi_dbg(""); + usbi_dbg(" "); + + ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->event_data_lock); - pending_events = usbi_pending_events(ctx); + event_flags = ctx->event_flags; ctx->event_flags |= USBI_EVENT_USER_INTERRUPT; - if (!pending_events) - usbi_signal_event(ctx); + if (!event_flags) + usbi_signal_event(&ctx->event); usbi_mutex_unlock(&ctx->event_data_lock); } @@ -1911,7 +1942,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) * * You only need to use this lock if you are developing an application * which calls poll() or select() on libusb's file descriptors directly, - * and may potentially be handling events from 2 threads simultaenously. + * and may potentially be handling events from 2 threads simultaneously. * If you stick to libusb's event handling loop functions (e.g. * libusb_handle_events()) then you do not need to be concerned with this * locking. @@ -1921,7 +1952,7 @@ void API_EXPORTED libusb_interrupt_event_handler(libusb_context *ctx) */ void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx) { - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->event_waiters_lock); } @@ -1932,7 +1963,7 @@ void API_EXPORTED libusb_lock_event_waiters(libusb_context *ctx) */ void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) { - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); usbi_mutex_unlock(&ctx->event_waiters_lock); } @@ -1959,25 +1990,28 @@ void API_EXPORTED libusb_unlock_event_waiters(libusb_context *ctx) * indicates unlimited timeout. * \returns 0 after a transfer completes or another thread stops event handling * \returns 1 if the timeout expired + * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid * \ref libusb_mtasync */ int API_EXPORTED libusb_wait_for_event(libusb_context *ctx, struct timeval *tv) { int r; - USBI_GET_CONTEXT(ctx); - if (tv == NULL) { + ctx = usbi_get_context(ctx); + if (!tv) { usbi_cond_wait(&ctx->event_waiters_cond, &ctx->event_waiters_lock); return 0; } + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + r = usbi_cond_timedwait(&ctx->event_waiters_cond, &ctx->event_waiters_lock, tv); - if (r < 0) - return r; - else - return (r == ETIMEDOUT); + return r == LIBUSB_ERROR_TIMEOUT; + + return 0; } static void handle_timeout(struct usbi_transfer *itransfer) @@ -1992,78 +2026,149 @@ static void handle_timeout(struct usbi_transfer *itransfer) itransfer->timeout_flags |= USBI_TRANSFER_TIMED_OUT; else usbi_warn(TRANSFER_CTX(transfer), - "async cancel failed %d errno=%d", r, errno); + "async cancel failed %d", r); } -static int handle_timeouts_locked(struct libusb_context *ctx) +static void handle_timeouts_locked(struct libusb_context *ctx) { - int r; - struct timespec systime_ts; - struct timeval systime; - struct usbi_transfer *transfer; + struct timespec systime; + struct usbi_transfer *itransfer; if (list_empty(&ctx->flying_transfers)) - return 0; + return; /* get current time */ - r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &systime_ts); - if (r < 0) - return r; - - TIMESPEC_TO_TIMEVAL(&systime, &systime_ts); + usbi_get_monotonic_time(&systime); /* iterate through flying transfers list, finding all transfers that * have expired timeouts */ - list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { - struct timeval *cur_tv = &transfer->timeout; + for_each_transfer(ctx, itransfer) { + struct timespec *cur_ts = &itransfer->timeout; /* if we've reached transfers of infinite timeout, we're all done */ - if (!timerisset(cur_tv)) - return 0; + if (!TIMESPEC_IS_SET(cur_ts)) + return; /* ignore timeouts we've already handled */ - if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) continue; /* if transfer has non-expired timeout, nothing more to do */ - if ((cur_tv->tv_sec > systime.tv_sec) || - (cur_tv->tv_sec == systime.tv_sec && - cur_tv->tv_usec > systime.tv_usec)) - return 0; + if (TIMESPEC_CMP(cur_ts, &systime, >)) + return; /* otherwise, we've got an expired timeout to handle */ - handle_timeout(transfer); + handle_timeout(itransfer); } - return 0; } -static int handle_timeouts(struct libusb_context *ctx) +static void handle_timeouts(struct libusb_context *ctx) { - int r; - USBI_GET_CONTEXT(ctx); + ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->flying_transfers_lock); - r = handle_timeouts_locked(ctx); + handle_timeouts_locked(ctx); usbi_mutex_unlock(&ctx->flying_transfers_lock); +} + +static int handle_event_trigger(struct libusb_context *ctx) +{ + struct list_head hotplug_msgs; + int r = 0; + + usbi_dbg("event triggered"); + + list_init(&hotplug_msgs); + + /* take the the event data lock while processing events */ + usbi_mutex_lock(&ctx->event_data_lock); + + /* check if someone modified the event sources */ + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) + usbi_dbg("someone updated the event sources"); + + if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { + usbi_dbg("someone purposefully interrupted"); + ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT; + } + + /* check if someone is closing a device */ + if (ctx->event_flags & USBI_EVENT_DEVICE_CLOSE) + usbi_dbg("someone is closing a device"); + + /* check for any pending hotplug messages */ + if (ctx->event_flags & USBI_EVENT_HOTPLUG_MSG_PENDING) { + usbi_dbg("hotplug message received"); + ctx->event_flags &= ~USBI_EVENT_HOTPLUG_MSG_PENDING; + assert(!list_empty(&ctx->hotplug_msgs)); + list_cut(&hotplug_msgs, &ctx->hotplug_msgs); + } + + /* complete any pending transfers */ + if (ctx->event_flags & USBI_EVENT_TRANSFER_COMPLETED) { + struct usbi_transfer *itransfer, *tmp; + struct list_head completed_transfers; + + assert(!list_empty(&ctx->completed_transfers)); + list_cut(&completed_transfers, &ctx->completed_transfers); + usbi_mutex_unlock(&ctx->event_data_lock); + + __for_each_completed_transfer_safe(&completed_transfers, itransfer, tmp) { + list_del(&itransfer->completed_list); + r = usbi_backend.handle_transfer_completion(itransfer); + if (r) { + usbi_err(ctx, "backend handle_transfer_completion failed with error %d", r); + break; + } + } + + usbi_mutex_lock(&ctx->event_data_lock); + if (!list_empty(&completed_transfers)) { + /* an error occurred, put the remaining transfers back on the list */ + list_splice_front(&completed_transfers, &ctx->completed_transfers); + } else if (list_empty(&ctx->completed_transfers)) { + ctx->event_flags &= ~USBI_EVENT_TRANSFER_COMPLETED; + } + } + + /* if no further pending events, clear the event */ + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); + + usbi_mutex_unlock(&ctx->event_data_lock); + + /* process the hotplug messages, if any */ + while (!list_empty(&hotplug_msgs)) { + struct libusb_hotplug_message *message = + list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list); + + usbi_hotplug_match(ctx, message->device, message->event); + + /* the device left, dereference the device */ + if (message->event == LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT) + libusb_unref_device(message->device); + + list_del(&message->list); + free(message); + } + return r; } -#ifdef USBI_TIMERFD_AVAILABLE -static int handle_timerfd_trigger(struct libusb_context *ctx) +#ifdef HAVE_OS_TIMER +static int handle_timer_trigger(struct libusb_context *ctx) { int r; usbi_mutex_lock(&ctx->flying_transfers_lock); /* process the timeout that just happened */ - r = handle_timeouts_locked(ctx); - if (r < 0) - goto out; + handle_timeouts_locked(ctx); - /* arm for next timeout*/ - r = arm_timerfd_for_next_timeout(ctx); + /* arm for next timeout */ + r = arm_timer_for_next_timeout(ctx); -out: usbi_mutex_unlock(&ctx->flying_transfers_lock); + return r; } #endif @@ -2072,73 +2177,38 @@ out: * doing the same thing. */ static int handle_events(struct libusb_context *ctx, struct timeval *tv) { - int r; - struct usbi_pollfd *ipollfd; - POLL_NFDS_TYPE nfds = 0; - POLL_NFDS_TYPE internal_nfds; - struct pollfd *fds = NULL; - int i = -1; - int timeout_ms; + struct usbi_reported_events reported_events; + int r, timeout_ms; /* prevent attempts to recursively handle events (e.g. calling into * libusb_handle_events() from within a hotplug or transfer callback) */ if (usbi_handling_events(ctx)) return LIBUSB_ERROR_BUSY; - usbi_start_event_handling(ctx); - /* there are certain fds that libusb uses internally, currently: - * - * 1) event pipe - * 2) timerfd - * - * the backend will never need to attempt to handle events on these fds, so - * we determine how many fds are in use internally for this context and when - * handle_events() is called in the backend, the pollfd list and count will - * be adjusted to skip over these internal fds */ - if (usbi_using_timerfd(ctx)) - internal_nfds = 2; - else - internal_nfds = 1; - - /* only reallocate the poll fds when the list of poll fds has been modified - * since the last poll, otherwise reuse them to save the additional overhead */ + /* only reallocate the event source data when the list of event sources has + * been modified since the last handle_events(), otherwise reuse them to + * save the additional overhead */ usbi_mutex_lock(&ctx->event_data_lock); - if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) { - usbi_dbg("poll fds modified, reallocating"); + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) { + usbi_dbg("event sources modified, reallocating event data"); - if (ctx->pollfds) { - free(ctx->pollfds); - ctx->pollfds = NULL; - } - - /* sanity check - it is invalid for a context to have fewer than the - * required internal fds (memory corruption?) */ - assert(ctx->pollfds_cnt >= internal_nfds); + /* free anything removed since we last ran */ + cleanup_removed_event_sources(ctx); - ctx->pollfds = calloc(ctx->pollfds_cnt, sizeof(*ctx->pollfds)); - if (!ctx->pollfds) { + r = usbi_alloc_event_data(ctx); + if (r) { usbi_mutex_unlock(&ctx->event_data_lock); - r = LIBUSB_ERROR_NO_MEM; - goto done; - } - - list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) { - struct libusb_pollfd *pollfd = &ipollfd->pollfd; - i++; - ctx->pollfds[i].fd = pollfd->fd; - ctx->pollfds[i].events = pollfd->events; + return r; } /* reset the flag now that we have the updated list */ - ctx->event_flags &= ~USBI_EVENT_POLLFDS_MODIFIED; + ctx->event_flags &= ~USBI_EVENT_EVENT_SOURCES_MODIFIED; - /* if no further pending events, clear the event pipe so that we do - * not immediately return from poll */ - if (!usbi_pending_events(ctx)) - usbi_clear_event(ctx); + /* if no further pending events, clear the event so that we do + * not immediately return from the wait function */ + if (!ctx->event_flags) + usbi_clear_event(&ctx->event); } - fds = ctx->pollfds; - nfds = ctx->pollfds_cnt; usbi_mutex_unlock(&ctx->event_data_lock); timeout_ms = (int)(tv->tv_sec * 1000) + (tv->tv_usec / 1000); @@ -2147,125 +2217,42 @@ static int handle_events(struct libusb_context *ctx, struct timeval *tv) if (tv->tv_usec % 1000) timeout_ms++; - usbi_dbg("poll() %d fds with timeout in %dms", nfds, timeout_ms); - r = usbi_poll(fds, nfds, timeout_ms); - usbi_dbg("poll() returned %d", r); - if (r == 0) { - r = handle_timeouts(ctx); - goto done; - } else if (r == -1 && errno == EINTR) { - r = LIBUSB_ERROR_INTERRUPTED; - goto done; - } else if (r < 0) { - usbi_err(ctx, "poll failed %d err=%d", r, errno); - r = LIBUSB_ERROR_IO; - goto done; - } - - /* fds[0] is always the event pipe */ - if (fds[0].revents) { - struct list_head hotplug_msgs; - struct usbi_transfer *itransfer; - int hotplug_cb_deregistered = 0; - int ret = 0; - - list_init(&hotplug_msgs); - - usbi_dbg("caught a fish on the event pipe"); - - /* take the the event data lock while processing events */ - usbi_mutex_lock(&ctx->event_data_lock); - - /* check if someone added a new poll fd */ - if (ctx->event_flags & USBI_EVENT_POLLFDS_MODIFIED) - usbi_dbg("someone updated the poll fds"); + reported_events.event_bits = 0; - if (ctx->event_flags & USBI_EVENT_USER_INTERRUPT) { - usbi_dbg("someone purposely interrupted"); - ctx->event_flags &= ~USBI_EVENT_USER_INTERRUPT; - } - - if (ctx->event_flags & USBI_EVENT_HOTPLUG_CB_DEREGISTERED) { - usbi_dbg("someone unregistered a hotplug cb"); - ctx->event_flags &= ~USBI_EVENT_HOTPLUG_CB_DEREGISTERED; - hotplug_cb_deregistered = 1; - } - - /* check if someone is closing a device */ - if (ctx->device_close) - usbi_dbg("someone is closing a device"); - - /* check for any pending hotplug messages */ - if (!list_empty(&ctx->hotplug_msgs)) { - usbi_dbg("hotplug message received"); - list_cut(&hotplug_msgs, &ctx->hotplug_msgs); - } - - /* complete any pending transfers */ - while (ret == 0 && !list_empty(&ctx->completed_transfers)) { - itransfer = list_first_entry(&ctx->completed_transfers, struct usbi_transfer, completed_list); - list_del(&itransfer->completed_list); - usbi_mutex_unlock(&ctx->event_data_lock); - ret = usbi_backend.handle_transfer_completion(itransfer); - if (ret) - usbi_err(ctx, "backend handle_transfer_completion failed with error %d", ret); - usbi_mutex_lock(&ctx->event_data_lock); - } - - /* if no further pending events, clear the event pipe */ - if (!usbi_pending_events(ctx)) - usbi_clear_event(ctx); - - usbi_mutex_unlock(&ctx->event_data_lock); - - if (hotplug_cb_deregistered) - usbi_hotplug_deregister(ctx, 0); - - /* process the hotplug messages, if any */ - while (!list_empty(&hotplug_msgs)) { - struct libusb_hotplug_message *message = - list_first_entry(&hotplug_msgs, struct libusb_hotplug_message, list); - - usbi_hotplug_match(ctx, message->device, message->event); - - /* the device left, dereference the device */ - if (LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT == message->event) - libusb_unref_device(message->device); + usbi_start_event_handling(ctx); - list_del(&message->list); - free(message); + r = usbi_wait_for_events(ctx, &reported_events, timeout_ms); + if (r != LIBUSB_SUCCESS) { + if (r == LIBUSB_ERROR_TIMEOUT) { + handle_timeouts(ctx); + r = LIBUSB_SUCCESS; } + goto done; + } - if (ret) { + if (reported_events.event_triggered) { + r = handle_event_trigger(ctx); + if (r) { /* return error code */ - r = ret; goto done; } - - if (0 == --r) - goto done; } -#ifdef USBI_TIMERFD_AVAILABLE - /* on timerfd configurations, fds[1] is the timerfd */ - if (usbi_using_timerfd(ctx) && fds[1].revents) { - /* timerfd indicates that a timeout has expired */ - int ret; - usbi_dbg("timerfd triggered"); - - ret = handle_timerfd_trigger(ctx); - if (ret < 0) { +#ifdef HAVE_OS_TIMER + if (reported_events.timer_triggered) { + r = handle_timer_trigger(ctx); + if (r) { /* return error code */ - r = ret; goto done; } - - if (0 == --r) - goto done; } #endif - r = usbi_backend.handle_events(ctx, fds + internal_nfds, nfds - internal_nfds, r); + if (!reported_events.num_ready) + goto done; + + r = usbi_backend.handle_events(ctx, reported_events.event_data, + reported_events.event_data_count, reported_events.num_ready); if (r) usbi_err(ctx, "backend handle_events failed with error %d", r); @@ -2324,7 +2311,9 @@ static int get_next_timeout(libusb_context *ctx, struct timeval *tv, * \param tv the maximum time to block waiting for events, or an all zero * timeval struct for non-blocking mode * \param completed pointer to completion integer to check, or NULL - * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \returns 0 on success + * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, @@ -2333,11 +2322,15 @@ int API_EXPORTED libusb_handle_events_timeout_completed(libusb_context *ctx, int r; struct timeval poll_timeout; - USBI_GET_CONTEXT(ctx); + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + + ctx = usbi_get_context(ctx); r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ - return handle_timeouts(ctx); + handle_timeouts(ctx); + return 0; } retry: @@ -2375,9 +2368,8 @@ already_done: if (r < 0) return r; else if (r == 1) - return handle_timeouts(ctx); - else - return 0; + handle_timeouts(ctx); + return 0; } /** \ingroup libusb_poll @@ -2404,7 +2396,7 @@ int API_EXPORTED libusb_handle_events_timeout(libusb_context *ctx, /** \ingroup libusb_poll * Handle any pending events in blocking mode. There is currently a timeout - * hardcoded at 60 seconds but we plan to make it unlimited in future. For + * hard-coded at 60 seconds but we plan to make it unlimited in future. For * finer control over whether this function is blocking or non-blocking, or * for control over the timeout, use libusb_handle_events_timeout_completed() * instead. @@ -2461,7 +2453,9 @@ int API_EXPORTED libusb_handle_events_completed(libusb_context *ctx, * \param ctx the context to operate on, or NULL for the default context * \param tv the maximum time to block waiting for events, or zero for * non-blocking mode - * \returns 0 on success, or a LIBUSB_ERROR code on failure + * \returns 0 on success + * \returns LIBUSB_ERROR_INVALID_PARAM if timeval is invalid + * \returns another LIBUSB_ERROR code on other failure * \ref libusb_mtasync */ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, @@ -2470,11 +2464,15 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, int r; struct timeval poll_timeout; - USBI_GET_CONTEXT(ctx); + if (!TIMEVAL_IS_VALID(tv)) + return LIBUSB_ERROR_INVALID_PARAM; + + ctx = usbi_get_context(ctx); r = get_next_timeout(ctx, tv, &poll_timeout); if (r) { /* timeout already expired */ - return handle_timeouts(ctx); + handle_timeouts(ctx); + return 0; } return handle_events(ctx, &poll_timeout); @@ -2510,13 +2508,8 @@ int API_EXPORTED libusb_handle_events_locked(libusb_context *ctx, */ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) { -#if defined(USBI_TIMERFD_AVAILABLE) - USBI_GET_CONTEXT(ctx); - return usbi_using_timerfd(ctx); -#else - UNUSED(ctx); - return 0; -#endif + ctx = usbi_get_context(ctx); + return usbi_using_timer(ctx); } /** \ingroup libusb_poll @@ -2550,14 +2543,12 @@ int API_EXPORTED libusb_pollfds_handle_timeouts(libusb_context *ctx) int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, struct timeval *tv) { - struct usbi_transfer *transfer; - struct timespec cur_ts; - struct timeval cur_tv; - struct timeval next_timeout = { 0, 0 }; - int r; + struct usbi_transfer *itransfer; + struct timespec systime; + struct timespec next_timeout = { 0, 0 }; - USBI_GET_CONTEXT(ctx); - if (usbi_using_timerfd(ctx)) + ctx = usbi_get_context(ctx); + if (usbi_using_timer(ctx)) return 0; usbi_mutex_lock(&ctx->flying_transfers_lock); @@ -2568,37 +2559,33 @@ int API_EXPORTED libusb_get_next_timeout(libusb_context *ctx, } /* find next transfer which hasn't already been processed as timed out */ - list_for_each_entry(transfer, &ctx->flying_transfers, list, struct usbi_transfer) { - if (transfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) + for_each_transfer(ctx, itransfer) { + if (itransfer->timeout_flags & (USBI_TRANSFER_TIMEOUT_HANDLED | USBI_TRANSFER_OS_HANDLES_TIMEOUT)) continue; - /* if we've reached transfers of infinte timeout, we're done looking */ - if (!timerisset(&transfer->timeout)) + /* if we've reached transfers of infinite timeout, we're done looking */ + if (!TIMESPEC_IS_SET(&itransfer->timeout)) break; - next_timeout = transfer->timeout; + next_timeout = itransfer->timeout; break; } usbi_mutex_unlock(&ctx->flying_transfers_lock); - if (!timerisset(&next_timeout)) { + if (!TIMESPEC_IS_SET(&next_timeout)) { usbi_dbg("no URB with timeout or all handled by OS; no timeout!"); return 0; } - r = usbi_backend.clock_gettime(USBI_CLOCK_MONOTONIC, &cur_ts); - if (r < 0) { - usbi_err(ctx, "failed to read monotonic clock, errno=%d", errno); - return 0; - } - TIMESPEC_TO_TIMEVAL(&cur_tv, &cur_ts); + usbi_get_monotonic_time(&systime); - if (!timercmp(&cur_tv, &next_timeout, <)) { + if (!TIMESPEC_CMP(&systime, &next_timeout, <)) { usbi_dbg("first timeout already expired"); timerclear(tv); } else { - timersub(&next_timeout, &cur_tv, tv); - usbi_dbg("next timeout in %d.%06ds", tv->tv_sec, tv->tv_usec); + TIMESPEC_SUB(&next_timeout, &systime, &next_timeout); + TIMESPEC_TO_TIMEVAL(tv, &next_timeout); + usbi_dbg("next timeout in %ld.%06lds", (long)tv->tv_sec, (long)tv->tv_usec); } return 1; @@ -2629,78 +2616,92 @@ void API_EXPORTED libusb_set_pollfd_notifiers(libusb_context *ctx, libusb_pollfd_added_cb added_cb, libusb_pollfd_removed_cb removed_cb, void *user_data) { - USBI_GET_CONTEXT(ctx); +#if !defined(PLATFORM_WINDOWS) + ctx = usbi_get_context(ctx); ctx->fd_added_cb = added_cb; ctx->fd_removed_cb = removed_cb; ctx->fd_cb_user_data = user_data; +#else + usbi_err(ctx, "external polling of libusb's internal event sources " \ + "is not yet supported on Windows"); + UNUSED(added_cb); + UNUSED(removed_cb); + UNUSED(user_data); +#endif } /* * Interrupt the iteration of the event handling thread, so that it picks - * up the fd change. Callers of this function must hold the event_data_lock. + * up the event source change. Callers of this function must hold the event_data_lock. */ -static void usbi_fd_notification(struct libusb_context *ctx) +static void usbi_event_source_notification(struct libusb_context *ctx) { - int pending_events; + unsigned int event_flags; /* Record that there is a new poll fd. * Only signal an event if there are no prior pending events. */ - pending_events = usbi_pending_events(ctx); - ctx->event_flags |= USBI_EVENT_POLLFDS_MODIFIED; - if (!pending_events) - usbi_signal_event(ctx); + event_flags = ctx->event_flags; + ctx->event_flags |= USBI_EVENT_EVENT_SOURCES_MODIFIED; + if (!event_flags) + usbi_signal_event(&ctx->event); } -/* Add a file descriptor to the list of file descriptors to be monitored. - * events should be specified as a bitmask of events passed to poll(), e.g. +/* Add an event source to the list of event sources to be monitored. + * poll_events should be specified as a bitmask of events passed to poll(), e.g. * POLLIN and/or POLLOUT. */ -int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events) +int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, short poll_events) { - struct usbi_pollfd *ipollfd = malloc(sizeof(*ipollfd)); - if (!ipollfd) + struct usbi_event_source *ievent_source = malloc(sizeof(*ievent_source)); + + if (!ievent_source) return LIBUSB_ERROR_NO_MEM; - usbi_dbg("add fd %d events %d", fd, events); - ipollfd->pollfd.fd = fd; - ipollfd->pollfd.events = events; + usbi_dbg("add " USBI_OS_HANDLE_FORMAT_STRING " events %d", os_handle, poll_events); + ievent_source->data.os_handle = os_handle; + ievent_source->data.poll_events = poll_events; usbi_mutex_lock(&ctx->event_data_lock); - list_add_tail(&ipollfd->list, &ctx->ipollfds); - ctx->pollfds_cnt++; - usbi_fd_notification(ctx); + list_add_tail(&ievent_source->list, &ctx->event_sources); + usbi_event_source_notification(ctx); usbi_mutex_unlock(&ctx->event_data_lock); +#if !defined(PLATFORM_WINDOWS) if (ctx->fd_added_cb) - ctx->fd_added_cb(fd, events, ctx->fd_cb_user_data); + ctx->fd_added_cb(os_handle, poll_events, ctx->fd_cb_user_data); +#endif + return 0; } -/* Remove a file descriptor from the list of file descriptors to be polled. */ -void usbi_remove_pollfd(struct libusb_context *ctx, int fd) +/* Remove an event source from the list of event sources to be monitored. */ +void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle) { - struct usbi_pollfd *ipollfd; + struct usbi_event_source *ievent_source; int found = 0; - usbi_dbg("remove fd %d", fd); + usbi_dbg("remove " USBI_OS_HANDLE_FORMAT_STRING, os_handle); usbi_mutex_lock(&ctx->event_data_lock); - list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) - if (ipollfd->pollfd.fd == fd) { + for_each_event_source(ctx, ievent_source) { + if (ievent_source->data.os_handle == os_handle) { found = 1; break; } + } if (!found) { - usbi_dbg("couldn't find fd %d to remove", fd); + usbi_dbg("couldn't find " USBI_OS_HANDLE_FORMAT_STRING " to remove", os_handle); usbi_mutex_unlock(&ctx->event_data_lock); return; } - list_del(&ipollfd->list); - ctx->pollfds_cnt--; - usbi_fd_notification(ctx); + list_del(&ievent_source->list); + list_add_tail(&ievent_source->list, &ctx->removed_event_sources); + usbi_event_source_notification(ctx); usbi_mutex_unlock(&ctx->event_data_lock); - free(ipollfd); + +#if !defined(PLATFORM_WINDOWS) if (ctx->fd_removed_cb) - ctx->fd_removed_cb(fd, ctx->fd_cb_user_data); + ctx->fd_removed_cb(os_handle, ctx->fd_cb_user_data); +#endif } /** \ingroup libusb_poll @@ -2722,28 +2723,36 @@ DEFAULT_VISIBILITY const struct libusb_pollfd ** LIBUSB_CALL libusb_get_pollfds( libusb_context *ctx) { -#ifndef OS_WINDOWS +#if !defined(PLATFORM_WINDOWS) struct libusb_pollfd **ret = NULL; - struct usbi_pollfd *ipollfd; - size_t i = 0; - USBI_GET_CONTEXT(ctx); + struct usbi_event_source *ievent_source; + size_t i; + + static_assert(sizeof(struct usbi_event_source_data) == sizeof(struct libusb_pollfd), + "mismatch between usbi_event_source_data and libusb_pollfd sizes"); + + ctx = usbi_get_context(ctx); usbi_mutex_lock(&ctx->event_data_lock); - ret = calloc(ctx->pollfds_cnt + 1, sizeof(struct libusb_pollfd *)); + i = 0; + for_each_event_source(ctx, ievent_source) + i++; + + ret = calloc(i + 1, sizeof(struct libusb_pollfd *)); if (!ret) goto out; - list_for_each_entry(ipollfd, &ctx->ipollfds, list, struct usbi_pollfd) - ret[i++] = (struct libusb_pollfd *) ipollfd; - ret[ctx->pollfds_cnt] = NULL; + i = 0; + for_each_event_source(ctx, ievent_source) + ret[i++] = (struct libusb_pollfd *)ievent_source; out: usbi_mutex_unlock(&ctx->event_data_lock); - return (const struct libusb_pollfd **) ret; + return (const struct libusb_pollfd **)ret; #else - usbi_err(ctx, "external polling of libusb's internal descriptors "\ - "is not yet supported on Windows platforms"); + usbi_err(ctx, "external polling of libusb's internal event sources " \ + "is not yet supported on Windows"); return NULL; #endif } @@ -2755,16 +2764,17 @@ out: * Since version 1.0.20, \ref LIBUSB_API_VERSION >= 0x01000104 * * It is legal to call this function with a NULL pollfd list. In this case, - * the function will simply return safely. + * the function will simply do nothing. * * \param pollfds the list of libusb_pollfd structures to free */ void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds) { - if (!pollfds) - return; - +#if !defined(PLATFORM_WINDOWS) free((void *)pollfds); +#else + UNUSED(pollfds); +#endif } /* Backends may call this from handle_events to report disconnection of a @@ -2773,6 +2783,7 @@ void API_EXPORTED libusb_free_pollfds(const struct libusb_pollfd **pollfds) */ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) { + struct libusb_context *ctx = HANDLE_CTX(dev_handle); struct usbi_transfer *cur; struct usbi_transfer *to_cancel; @@ -2794,8 +2805,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) while (1) { to_cancel = NULL; - usbi_mutex_lock(&HANDLE_CTX(dev_handle)->flying_transfers_lock); - list_for_each_entry(cur, &HANDLE_CTX(dev_handle)->flying_transfers, list, struct usbi_transfer) + usbi_mutex_lock(&ctx->flying_transfers_lock); + for_each_transfer(ctx, cur) { if (USBI_TRANSFER_TO_LIBUSB_TRANSFER(cur)->dev_handle == dev_handle) { usbi_mutex_lock(&cur->lock); if (cur->state_flags & USBI_TRANSFER_IN_FLIGHT) @@ -2805,7 +2816,8 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) if (to_cancel) break; } - usbi_mutex_unlock(&HANDLE_CTX(dev_handle)->flying_transfers_lock); + } + usbi_mutex_unlock(&ctx->flying_transfers_lock); if (!to_cancel) break; @@ -2818,5 +2830,4 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle) usbi_mutex_unlock(&to_cancel->lock); usbi_handle_transfer_completion(to_cancel, LIBUSB_TRANSFER_NO_DEVICE); } - } diff --git a/mac/libusb/libusb.h b/mac/libusb/libusb.h index 430136b2e..1308571cd 100644 --- a/mac/libusb/libusb.h +++ b/mac/libusb/libusb.h @@ -3,7 +3,8 @@ * Copyright © 2001 Johannes Erdfelt * Copyright © 2007-2008 Daniel Drake * Copyright © 2012 Pete Batard - * Copyright © 2012 Nathan Hjelm + * Copyright © 2012-2018 Nathan Hjelm + * Copyright © 2014-2020 Chris Dickens * For more information, please visit: http://libusb.info * * This library is free software; you can redistribute it and/or @@ -24,55 +25,36 @@ #ifndef LIBUSB_H #define LIBUSB_H -#ifdef _MSC_VER +#if defined(_MSC_VER) /* on MS environments, the inline keyword is available in C++ only */ #if !defined(__cplusplus) #define inline __inline #endif -/* ssize_t is also not available (copy/paste from MinGW) */ -#ifndef _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED -#undef ssize_t -#ifdef _WIN64 - typedef __int64 ssize_t; -#else - typedef int ssize_t; -#endif /* _WIN64 */ -#endif /* _SSIZE_T_DEFINED */ +/* ssize_t is also not available */ +#include +typedef SSIZE_T ssize_t; #endif /* _MSC_VER */ -/* stdint.h is not available on older MSVC */ -#if defined(_MSC_VER) && (_MSC_VER < 1600) && (!defined(_STDINT)) && (!defined(_STDINT_H)) -typedef unsigned __int8 uint8_t; -typedef unsigned __int16 uint16_t; -typedef unsigned __int32 uint32_t; -#else +#include #include -#endif - -#if !defined(_WIN32_WCE) #include -#endif - -#if defined(__linux__) || defined(__APPLE__) || defined(__CYGWIN__) || defined(__HAIKU__) +#if !defined(_MSC_VER) #include #endif - #include -#include #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) #define ZERO_SIZED_ARRAY /* [] - valid C99 code */ #else #define ZERO_SIZED_ARRAY 0 /* [0] - non-standard, but usually working code */ -#endif +#endif /* __STDC_VERSION__ */ /* 'interface' might be defined as a macro on Windows, so we need to * undefine it so as not to break the current libusb API, because * libusb_config_descriptor has an 'interface' member * As this can be problematic if you include windows.h after libusb.h * in your sources, we force windows.h to be included first. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(__CYGWIN__) #include #if defined(interface) #undef interface @@ -80,17 +62,22 @@ typedef unsigned __int32 uint32_t; #if !defined(__CYGWIN__) #include #endif -#endif +#endif /* _WIN32 || __CYGWIN__ */ -#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) -#define LIBUSB_DEPRECATED_FOR(f) \ - __attribute__((deprecated("Use " #f " instead"))) -#elif __GNUC__ >= 3 -#define LIBUSB_DEPRECATED_FOR(f) __attribute__((deprecated)) +#if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated ("Use " #f " instead"))) +#elif defined(__GNUC__) && (__GNUC__ >= 3) +#define LIBUSB_DEPRECATED_FOR(f) __attribute__ ((deprecated)) #else #define LIBUSB_DEPRECATED_FOR(f) #endif /* __GNUC__ */ +#if defined(__GNUC__) +#define LIBUSB_PACKED __attribute__ ((packed)) +#else +#define LIBUSB_PACKED +#endif /* __GNUC__ */ + /** \def LIBUSB_CALL * \ingroup libusb_misc * libusb's Windows calling convention. @@ -123,11 +110,11 @@ typedef unsigned __int32 uint32_t; * return type, before the function name. See internal documentation for * API_EXPORTED. */ -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#if defined(_WIN32) || defined(__CYGWIN__) #define LIBUSB_CALL WINAPI #else #define LIBUSB_CALL -#endif +#endif /* _WIN32 || __CYGWIN__ */ /** \def LIBUSB_API_VERSION * \ingroup libusb_misc @@ -149,12 +136,12 @@ typedef unsigned __int32 uint32_t; * Internally, LIBUSB_API_VERSION is defined as follows: * (libusb major << 24) | (libusb minor << 16) | (16 bit incremental) */ -#define LIBUSB_API_VERSION 0x01000106 +#define LIBUSB_API_VERSION 0x01000108 /* The following is kept for compatibility, but will be deprecated in the future */ #define LIBUSBX_API_VERSION LIBUSB_API_VERSION -#ifdef __cplusplus +#if defined(__cplusplus) extern "C" { #endif @@ -196,35 +183,35 @@ enum libusb_class_code { * this bDeviceClass value indicates that each interface specifies its * own class information and all interfaces operate independently. */ - LIBUSB_CLASS_PER_INTERFACE = 0, + LIBUSB_CLASS_PER_INTERFACE = 0x00, /** Audio class */ - LIBUSB_CLASS_AUDIO = 1, + LIBUSB_CLASS_AUDIO = 0x01, /** Communications class */ - LIBUSB_CLASS_COMM = 2, + LIBUSB_CLASS_COMM = 0x02, /** Human Interface Device class */ - LIBUSB_CLASS_HID = 3, + LIBUSB_CLASS_HID = 0x03, /** Physical */ - LIBUSB_CLASS_PHYSICAL = 5, - - /** Printer class */ - LIBUSB_CLASS_PRINTER = 7, + LIBUSB_CLASS_PHYSICAL = 0x05, /** Image class */ - LIBUSB_CLASS_PTP = 6, /* legacy name from libusb-0.1 usb.h */ - LIBUSB_CLASS_IMAGE = 6, + LIBUSB_CLASS_IMAGE = 0x06, + LIBUSB_CLASS_PTP = 0x06, /* legacy name from libusb-0.1 usb.h */ + + /** Printer class */ + LIBUSB_CLASS_PRINTER = 0x07, /** Mass storage class */ - LIBUSB_CLASS_MASS_STORAGE = 8, + LIBUSB_CLASS_MASS_STORAGE = 0x08, /** Hub class */ - LIBUSB_CLASS_HUB = 9, + LIBUSB_CLASS_HUB = 0x09, /** Data class */ - LIBUSB_CLASS_DATA = 10, + LIBUSB_CLASS_DATA = 0x0a, /** Smart Card */ LIBUSB_CLASS_SMART_CARD = 0x0b, @@ -244,6 +231,9 @@ enum libusb_class_code { /** Wireless class */ LIBUSB_CLASS_WIRELESS = 0xe0, + /** Miscellaneous class */ + LIBUSB_CLASS_MISCELLANEOUS = 0xef, + /** Application class */ LIBUSB_CLASS_APPLICATION = 0xfe, @@ -311,12 +301,13 @@ enum libusb_descriptor_type { #define LIBUSB_BT_CONTAINER_ID_SIZE 20 /* We unwrap the BOS => define its max size */ -#define LIBUSB_DT_BOS_MAX_SIZE ((LIBUSB_DT_BOS_SIZE) +\ - (LIBUSB_BT_USB_2_0_EXTENSION_SIZE) +\ - (LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE) +\ - (LIBUSB_BT_CONTAINER_ID_SIZE)) +#define LIBUSB_DT_BOS_MAX_SIZE \ + (LIBUSB_DT_BOS_SIZE + \ + LIBUSB_BT_USB_2_0_EXTENSION_SIZE + \ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY_SIZE + \ + LIBUSB_BT_CONTAINER_ID_SIZE) -#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ +#define LIBUSB_ENDPOINT_ADDRESS_MASK 0x0f /* in bEndpointAddress */ #define LIBUSB_ENDPOINT_DIR_MASK 0x80 /** \ingroup libusb_desc @@ -324,34 +315,31 @@ enum libusb_descriptor_type { * \ref libusb_endpoint_descriptor::bEndpointAddress "endpoint address" scheme. */ enum libusb_endpoint_direction { - /** In: device-to-host */ - LIBUSB_ENDPOINT_IN = 0x80, - /** Out: host-to-device */ - LIBUSB_ENDPOINT_OUT = 0x00 + LIBUSB_ENDPOINT_OUT = 0x00, + + /** In: device-to-host */ + LIBUSB_ENDPOINT_IN = 0x80 }; -#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ +#define LIBUSB_TRANSFER_TYPE_MASK 0x03 /* in bmAttributes */ /** \ingroup libusb_desc * Endpoint transfer type. Values for bits 0:1 of the * \ref libusb_endpoint_descriptor::bmAttributes "endpoint attributes" field. */ -enum libusb_transfer_type { +enum libusb_endpoint_transfer_type { /** Control endpoint */ - LIBUSB_TRANSFER_TYPE_CONTROL = 0, + LIBUSB_ENDPOINT_TRANSFER_TYPE_CONTROL = 0x0, /** Isochronous endpoint */ - LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1, + LIBUSB_ENDPOINT_TRANSFER_TYPE_ISOCHRONOUS = 0x1, /** Bulk endpoint */ - LIBUSB_TRANSFER_TYPE_BULK = 2, + LIBUSB_ENDPOINT_TRANSFER_TYPE_BULK = 0x2, /** Interrupt endpoint */ - LIBUSB_TRANSFER_TYPE_INTERRUPT = 3, - - /** Stream endpoint */ - LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4, + LIBUSB_ENDPOINT_TRANSFER_TYPE_INTERRUPT = 0x3 }; /** \ingroup libusb_misc @@ -386,20 +374,20 @@ enum libusb_standard_request { LIBUSB_REQUEST_SET_CONFIGURATION = 0x09, /** Return the selected alternate setting for the specified interface */ - LIBUSB_REQUEST_GET_INTERFACE = 0x0A, + LIBUSB_REQUEST_GET_INTERFACE = 0x0a, /** Select an alternate interface for the specified interface */ - LIBUSB_REQUEST_SET_INTERFACE = 0x0B, + LIBUSB_REQUEST_SET_INTERFACE = 0x0b, /** Set then report an endpoint's synchronization frame */ - LIBUSB_REQUEST_SYNCH_FRAME = 0x0C, + LIBUSB_REQUEST_SYNCH_FRAME = 0x0c, /** Sets both the U1 and U2 Exit Latency */ LIBUSB_REQUEST_SET_SEL = 0x30, /** Delay from the time a host transmits a packet to the time it is * received by the device. */ - LIBUSB_SET_ISOCH_DELAY = 0x31, + LIBUSB_SET_ISOCH_DELAY = 0x31 }; /** \ingroup libusb_misc @@ -435,10 +423,10 @@ enum libusb_request_recipient { LIBUSB_RECIPIENT_ENDPOINT = 0x02, /** Other */ - LIBUSB_RECIPIENT_OTHER = 0x03, + LIBUSB_RECIPIENT_OTHER = 0x03 }; -#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0C +#define LIBUSB_ISO_SYNC_TYPE_MASK 0x0c /** \ingroup libusb_desc * Synchronization type for isochronous endpoints. Values for bits 2:3 of the @@ -447,19 +435,19 @@ enum libusb_request_recipient { */ enum libusb_iso_sync_type { /** No synchronization */ - LIBUSB_ISO_SYNC_TYPE_NONE = 0, + LIBUSB_ISO_SYNC_TYPE_NONE = 0x0, /** Asynchronous */ - LIBUSB_ISO_SYNC_TYPE_ASYNC = 1, + LIBUSB_ISO_SYNC_TYPE_ASYNC = 0x1, /** Adaptive */ - LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 2, + LIBUSB_ISO_SYNC_TYPE_ADAPTIVE = 0x2, /** Synchronous */ - LIBUSB_ISO_SYNC_TYPE_SYNC = 3 + LIBUSB_ISO_SYNC_TYPE_SYNC = 0x3 }; -#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 +#define LIBUSB_ISO_USAGE_TYPE_MASK 0x30 /** \ingroup libusb_desc * Usage type for isochronous endpoints. Values for bits 4:5 of the @@ -468,13 +456,68 @@ enum libusb_iso_sync_type { */ enum libusb_iso_usage_type { /** Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_DATA = 0, + LIBUSB_ISO_USAGE_TYPE_DATA = 0x0, /** Feedback endpoint */ - LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 1, + LIBUSB_ISO_USAGE_TYPE_FEEDBACK = 0x1, /** Implicit feedback Data endpoint */ - LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 2, + LIBUSB_ISO_USAGE_TYPE_IMPLICIT = 0x2 +}; + +/** \ingroup libusb_desc + * Supported speeds (wSpeedSupported) bitfield. Indicates what + * speeds the device supports. + */ +enum libusb_supported_speed { + /** Low speed operation supported (1.5MBit/s). */ + LIBUSB_LOW_SPEED_OPERATION = (1 << 0), + + /** Full speed operation supported (12MBit/s). */ + LIBUSB_FULL_SPEED_OPERATION = (1 << 1), + + /** High speed operation supported (480MBit/s). */ + LIBUSB_HIGH_SPEED_OPERATION = (1 << 2), + + /** Superspeed operation supported (5000MBit/s). */ + LIBUSB_SUPER_SPEED_OPERATION = (1 << 3) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field + * of the USB 2.0 Extension descriptor. + */ +enum libusb_usb_2_0_extension_attributes { + /** Supports Link Power Management (LPM) */ + LIBUSB_BM_LPM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * Masks for the bits of the + * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field + * field of the SuperSpeed USB Device Capability descriptor. + */ +enum libusb_ss_usb_device_capability_attributes { + /** Supports Latency Tolerance Messages (LTM) */ + LIBUSB_BM_LTM_SUPPORT = (1 << 1) +}; + +/** \ingroup libusb_desc + * USB capability types + */ +enum libusb_bos_type { + /** Wireless USB device capability */ + LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 0x01, + + /** USB 2.0 extensions */ + LIBUSB_BT_USB_2_0_EXTENSION = 0x02, + + /** SuperSpeed USB device capability */ + LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 0x03, + + /** Container ID type */ + LIBUSB_BT_CONTAINER_ID = 0x04 }; /** \ingroup libusb_desc @@ -547,17 +590,15 @@ struct libusb_endpoint_descriptor { /** The address of the endpoint described by this descriptor. Bits 0:3 are * the endpoint number. Bits 4:6 are reserved. Bit 7 indicates direction, - * see \ref libusb_endpoint_direction. - */ + * see \ref libusb_endpoint_direction. */ uint8_t bEndpointAddress; /** Attributes which apply to the endpoint when it is configured using * the bConfigurationValue. Bits 0:1 determine the transfer type and - * correspond to \ref libusb_transfer_type. Bits 2:3 are only used for - * isochronous endpoints and correspond to \ref libusb_iso_sync_type. + * correspond to \ref libusb_endpoint_transfer_type. Bits 2:3 are only used + * for isochronous endpoints and correspond to \ref libusb_iso_sync_type. * Bits 4:5 are also only used for isochronous endpoints and correspond to - * \ref libusb_iso_usage_type. Bits 6:7 are reserved. - */ + * \ref libusb_iso_usage_type. Bits 6:7 are reserved. */ uint8_t bmAttributes; /** Maximum packet size this endpoint is capable of sending/receiving. */ @@ -577,7 +618,7 @@ struct libusb_endpoint_descriptor { * it will store them here, should you wish to parse them. */ const unsigned char *extra; - /** Length of the extra descriptors, in bytes. */ + /** Length of the extra descriptors, in bytes. Must be non-negative. */ int extra_length; }; @@ -627,7 +668,7 @@ struct libusb_interface_descriptor { * it will store them here, should you wish to parse them. */ const unsigned char *extra; - /** Length of the extra descriptors, in bytes. */ + /** Length of the extra descriptors, in bytes. Must be non-negative. */ int extra_length; }; @@ -639,7 +680,8 @@ struct libusb_interface { * by the num_altsetting field. */ const struct libusb_interface_descriptor *altsetting; - /** The number of alternate settings that belong to this interface */ + /** The number of alternate settings that belong to this interface. + * Must be non-negative. */ int num_altsetting; }; @@ -686,7 +728,7 @@ struct libusb_config_descriptor { * descriptors, it will store them here, should you wish to parse them. */ const unsigned char *extra; - /** Length of the extra descriptors, in bytes. */ + /** Length of the extra descriptors, in bytes. Must be non-negative. */ int extra_length; }; @@ -697,7 +739,6 @@ struct libusb_config_descriptor { * host-endian format. */ struct libusb_ss_endpoint_companion_descriptor { - /** Size of this descriptor (in bytes) */ uint8_t bLength; @@ -706,19 +747,18 @@ struct libusb_ss_endpoint_companion_descriptor { * this context. */ uint8_t bDescriptorType; - /** The maximum number of packets the endpoint can send or * receive as part of a burst. */ uint8_t bMaxBurst; - /** In bulk EP: bits 4:0 represents the maximum number of - * streams the EP supports. In isochronous EP: bits 1:0 - * represents the Mult - a zero based value that determines - * the maximum number of packets within a service interval */ + /** In bulk EP: bits 4:0 represents the maximum number of + * streams the EP supports. In isochronous EP: bits 1:0 + * represents the Mult - a zero based value that determines + * the maximum number of packets within a service interval */ uint8_t bmAttributes; - /** The total number of bytes this EP will transfer every - * service interval. valid only for periodic EPs. */ + /** The total number of bytes this EP will transfer every + * service interval. Valid only for periodic EPs. */ uint16_t wBytesPerInterval; }; @@ -729,15 +769,18 @@ struct libusb_ss_endpoint_companion_descriptor { */ struct libusb_bos_dev_capability_descriptor { /** Size of this descriptor (in bytes) */ - uint8_t bLength; + uint8_t bLength; + /** Descriptor type. Will have value * \ref libusb_descriptor_type::LIBUSB_DT_DEVICE_CAPABILITY * LIBUSB_DT_DEVICE_CAPABILITY in this context. */ - uint8_t bDescriptorType; + uint8_t bDescriptorType; + /** Device Capability type */ - uint8_t bDevCapabilityType; + uint8_t bDevCapabilityType; + /** Device Capability data (bLength - 3 bytes) */ - uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; + uint8_t dev_capability_data[ZERO_SIZED_ARRAY]; }; /** \ingroup libusb_desc @@ -788,7 +831,7 @@ struct libusb_usb_2_0_extension_descriptor { * A value of one in a bit location indicates a feature is * supported; a value of zero indicates it is not supported. * See \ref libusb_usb_2_0_extension_attributes. */ - uint32_t bmAttributes; + uint32_t bmAttributes; }; /** \ingroup libusb_desc @@ -853,7 +896,7 @@ struct libusb_container_id_descriptor { uint8_t bDevCapabilityType; /** Reserved field */ - uint8_t bReserved; + uint8_t bReserved; /** 128 bit UUID */ uint8_t ContainerID[16]; @@ -861,6 +904,9 @@ struct libusb_container_id_descriptor { /** \ingroup libusb_asyncio * Setup packet for control transfers. */ +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif struct libusb_control_setup { /** Request type. Bits 0:4 determine recipient, see * \ref libusb_request_recipient. Bits 5:6 determine type, see @@ -885,7 +931,10 @@ struct libusb_control_setup { /** Number of bytes to transfer */ uint16_t wLength; -}; +} LIBUSB_PACKED; +#if defined(_MSC_VER) +#pragma pack(pop) +#endif #define LIBUSB_CONTROL_SETUP_SIZE (sizeof(struct libusb_control_setup)) @@ -915,7 +964,7 @@ struct libusb_version { const char *rc; /** For ABI compatibility only. */ - const char* describe; + const char *describe; }; /** \ingroup libusb_lib @@ -985,62 +1034,7 @@ enum libusb_speed { LIBUSB_SPEED_SUPER = 4, /** The device is operating at super speed plus (10000MBit/s). */ - LIBUSB_SPEED_SUPER_PLUS = 5, -}; - -/** \ingroup libusb_dev - * Supported speeds (wSpeedSupported) bitfield. Indicates what - * speeds the device supports. - */ -enum libusb_supported_speed { - /** Low speed operation supported (1.5MBit/s). */ - LIBUSB_LOW_SPEED_OPERATION = 1, - - /** Full speed operation supported (12MBit/s). */ - LIBUSB_FULL_SPEED_OPERATION = 2, - - /** High speed operation supported (480MBit/s). */ - LIBUSB_HIGH_SPEED_OPERATION = 4, - - /** Superspeed operation supported (5000MBit/s). */ - LIBUSB_SUPER_SPEED_OPERATION = 8, -}; - -/** \ingroup libusb_dev - * Masks for the bits of the - * \ref libusb_usb_2_0_extension_descriptor::bmAttributes "bmAttributes" field - * of the USB 2.0 Extension descriptor. - */ -enum libusb_usb_2_0_extension_attributes { - /** Supports Link Power Management (LPM) */ - LIBUSB_BM_LPM_SUPPORT = 2, -}; - -/** \ingroup libusb_dev - * Masks for the bits of the - * \ref libusb_ss_usb_device_capability_descriptor::bmAttributes "bmAttributes" field - * field of the SuperSpeed USB Device Capability descriptor. - */ -enum libusb_ss_usb_device_capability_attributes { - /** Supports Latency Tolerance Messages (LTM) */ - LIBUSB_BM_LTM_SUPPORT = 2, -}; - -/** \ingroup libusb_dev - * USB capability types - */ -enum libusb_bos_type { - /** Wireless USB device capability */ - LIBUSB_BT_WIRELESS_USB_DEVICE_CAPABILITY = 1, - - /** USB 2.0 extensions */ - LIBUSB_BT_USB_2_0_EXTENSION = 2, - - /** SuperSpeed USB device capability */ - LIBUSB_BT_SS_USB_DEVICE_CAPABILITY = 3, - - /** Container ID type */ - LIBUSB_BT_CONTAINER_ID = 4, + LIBUSB_SPEED_SUPER_PLUS = 5 }; /** \ingroup libusb_misc @@ -1094,12 +1088,31 @@ enum libusb_error { message strings in strerror.c when adding new error codes here. */ /** Other error */ - LIBUSB_ERROR_OTHER = -99, + LIBUSB_ERROR_OTHER = -99 }; /* Total number of error codes in enum libusb_error */ #define LIBUSB_ERROR_COUNT 14 +/** \ingroup libusb_asyncio + * Transfer type */ +enum libusb_transfer_type { + /** Control transfer */ + LIBUSB_TRANSFER_TYPE_CONTROL = 0U, + + /** Isochronous transfer */ + LIBUSB_TRANSFER_TYPE_ISOCHRONOUS = 1U, + + /** Bulk transfer */ + LIBUSB_TRANSFER_TYPE_BULK = 2U, + + /** Interrupt transfer */ + LIBUSB_TRANSFER_TYPE_INTERRUPT = 3U, + + /** Bulk stream transfer */ + LIBUSB_TRANSFER_TYPE_BULK_STREAM = 4U +}; + /** \ingroup libusb_asyncio * Transfer status codes */ enum libusb_transfer_status { @@ -1124,7 +1137,7 @@ enum libusb_transfer_status { LIBUSB_TRANSFER_NO_DEVICE, /** Device sent more data than requested */ - LIBUSB_TRANSFER_OVERFLOW, + LIBUSB_TRANSFER_OVERFLOW /* NB! Remember to update libusb_error_name() when adding new status codes here. */ @@ -1134,19 +1147,19 @@ enum libusb_transfer_status { * libusb_transfer.flags values */ enum libusb_transfer_flags { /** Report short frames as errors */ - LIBUSB_TRANSFER_SHORT_NOT_OK = 1<<0, + LIBUSB_TRANSFER_SHORT_NOT_OK = (1U << 0), /** Automatically free() transfer buffer during libusb_free_transfer(). * Note that buffers allocated with libusb_dev_mem_alloc() should not * be attempted freed in this way, since free() is not an appropriate * way to release such memory. */ - LIBUSB_TRANSFER_FREE_BUFFER = 1<<1, + LIBUSB_TRANSFER_FREE_BUFFER = (1U << 1), /** Automatically call libusb_free_transfer() after callback returns. * If this flag is set, it is illegal to call libusb_free_transfer() * from your transfer callback, as this will result in a double-free * when this flag is acted upon. */ - LIBUSB_TRANSFER_FREE_TRANSFER = 1<<2, + LIBUSB_TRANSFER_FREE_TRANSFER = (1U << 2), /** Terminate transfers that are a multiple of the endpoint's * wMaxPacketSize with an extra zero length packet. This is useful @@ -1171,7 +1184,7 @@ enum libusb_transfer_flags { * * Available since libusb-1.0.9. */ - LIBUSB_TRANSFER_ADD_ZERO_PACKET = 1 << 3, + LIBUSB_TRANSFER_ADD_ZERO_PACKET = (1U << 3) }; /** \ingroup libusb_asyncio @@ -1216,7 +1229,7 @@ struct libusb_transfer { /** Address of the endpoint where this transfer will be sent. */ unsigned char endpoint; - /** Type of the endpoint from \ref libusb_transfer_type */ + /** Type of the transfer from \ref libusb_transfer_type */ unsigned char type; /** Timeout for this transfer in milliseconds. A value of 0 indicates no @@ -1232,7 +1245,7 @@ struct libusb_transfer { * to determine if errors occurred. */ enum libusb_transfer_status status; - /** Length of the data buffer */ + /** Length of the data buffer. Must be non-negative. */ int length; /** Actual length of data that was transferred. Read-only, and only for @@ -1244,14 +1257,23 @@ struct libusb_transfer { * fails, or is cancelled. */ libusb_transfer_cb_fn callback; - /** User context data to pass to the callback function. */ + /** User context data. Useful for associating specific data to a transfer + * that can be accessed from within the callback function. + * + * This field may be set manually or is taken as the `user_data` parameter + * of the following functions: + * - libusb_fill_bulk_transfer() + * - libusb_fill_bulk_stream_transfer() + * - libusb_fill_control_transfer() + * - libusb_fill_interrupt_transfer() + * - libusb_fill_iso_transfer() */ void *user_data; /** Data buffer */ unsigned char *buffer; /** Number of isochronous packets. Only used for I/O with isochronous - * endpoints. */ + * endpoints. Must be non-negative. */ int num_iso_packets; /** Isochronous packet descriptors, for isochronous transfers only. */ @@ -1265,44 +1287,75 @@ struct libusb_transfer { */ enum libusb_capability { /** The libusb_has_capability() API is available. */ - LIBUSB_CAP_HAS_CAPABILITY = 0x0000, + LIBUSB_CAP_HAS_CAPABILITY = 0x0000U, + /** Hotplug support is available on this platform. */ - LIBUSB_CAP_HAS_HOTPLUG = 0x0001, + LIBUSB_CAP_HAS_HOTPLUG = 0x0001U, + /** The library can access HID devices without requiring user intervention. * Note that before being able to actually access an HID device, you may * still have to call additional libusb functions such as * \ref libusb_detach_kernel_driver(). */ - LIBUSB_CAP_HAS_HID_ACCESS = 0x0100, - /** The library supports detaching of the default USB driver, using + LIBUSB_CAP_HAS_HID_ACCESS = 0x0100U, + + /** The library supports detaching of the default USB driver, using * \ref libusb_detach_kernel_driver(), if one is set by the OS kernel */ - LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101 + LIBUSB_CAP_SUPPORTS_DETACH_KERNEL_DRIVER = 0x0101U }; /** \ingroup libusb_lib * Log message levels. - * - LIBUSB_LOG_LEVEL_NONE (0) : no messages ever printed by the library (default) - * - LIBUSB_LOG_LEVEL_ERROR (1) : error messages are printed to stderr - * - LIBUSB_LOG_LEVEL_WARNING (2) : warning and error messages are printed to stderr - * - LIBUSB_LOG_LEVEL_INFO (3) : informational messages are printed to stderr - * - LIBUSB_LOG_LEVEL_DEBUG (4) : debug and informational messages are printed to stderr */ enum libusb_log_level { + /** (0) : No messages ever emitted by the library (default) */ LIBUSB_LOG_LEVEL_NONE = 0, + + /** (1) : Error messages are emitted */ LIBUSB_LOG_LEVEL_ERROR = 1, + + /** (2) : Warning and error messages are emitted */ LIBUSB_LOG_LEVEL_WARNING = 2, + + /** (3) : Informational, warning and error messages are emitted */ LIBUSB_LOG_LEVEL_INFO = 3, - LIBUSB_LOG_LEVEL_DEBUG = 4, + + /** (4) : All messages are emitted */ + LIBUSB_LOG_LEVEL_DEBUG = 4 }; +/** \ingroup libusb_lib + * Log callback mode. + * \see libusb_set_log_cb() + */ +enum libusb_log_cb_mode { + /** Callback function handling all log messages. */ + LIBUSB_LOG_CB_GLOBAL = (1 << 0), + + /** Callback function handling context related log messages. */ + LIBUSB_LOG_CB_CONTEXT = (1 << 1) +}; + +/** \ingroup libusb_lib + * Callback function for handling log messages. + * \param ctx the context which is related to the log message, or NULL if it + * is a global log message + * \param level the log level, see \ref libusb_log_level for a description + * \param str the log message + * \see libusb_set_log_cb() + */ +typedef void (LIBUSB_CALL *libusb_log_cb)(libusb_context *ctx, + enum libusb_log_level level, const char *str); + int LIBUSB_CALL libusb_init(libusb_context **ctx); void LIBUSB_CALL libusb_exit(libusb_context *ctx); LIBUSB_DEPRECATED_FOR(libusb_set_option) void LIBUSB_CALL libusb_set_debug(libusb_context *ctx, int level); +void LIBUSB_CALL libusb_set_log_cb(libusb_context *ctx, libusb_log_cb cb, int mode); const struct libusb_version * LIBUSB_CALL libusb_get_version(void); int LIBUSB_CALL libusb_has_capability(uint32_t capability); const char * LIBUSB_CALL libusb_error_name(int errcode); int LIBUSB_CALL libusb_setlocale(const char *locale); -const char * LIBUSB_CALL libusb_strerror(enum libusb_error errcode); +const char * LIBUSB_CALL libusb_strerror(int errcode); ssize_t LIBUSB_CALL libusb_get_device_list(libusb_context *ctx, libusb_device ***list); @@ -1324,7 +1377,7 @@ int LIBUSB_CALL libusb_get_config_descriptor_by_value(libusb_device *dev, void LIBUSB_CALL libusb_free_config_descriptor( struct libusb_config_descriptor *config); int LIBUSB_CALL libusb_get_ss_endpoint_companion_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, const struct libusb_endpoint_descriptor *endpoint, struct libusb_ss_endpoint_companion_descriptor **ep_comp); void LIBUSB_CALL libusb_free_ss_endpoint_companion_descriptor( @@ -1333,27 +1386,27 @@ int LIBUSB_CALL libusb_get_bos_descriptor(libusb_device_handle *dev_handle, struct libusb_bos_descriptor **bos); void LIBUSB_CALL libusb_free_bos_descriptor(struct libusb_bos_descriptor *bos); int LIBUSB_CALL libusb_get_usb_2_0_extension_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_usb_2_0_extension_descriptor **usb_2_0_extension); void LIBUSB_CALL libusb_free_usb_2_0_extension_descriptor( struct libusb_usb_2_0_extension_descriptor *usb_2_0_extension); int LIBUSB_CALL libusb_get_ss_usb_device_capability_descriptor( - struct libusb_context *ctx, + libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_ss_usb_device_capability_descriptor **ss_usb_device_cap); void LIBUSB_CALL libusb_free_ss_usb_device_capability_descriptor( struct libusb_ss_usb_device_capability_descriptor *ss_usb_device_cap); -int LIBUSB_CALL libusb_get_container_id_descriptor(struct libusb_context *ctx, +int LIBUSB_CALL libusb_get_container_id_descriptor(libusb_context *ctx, struct libusb_bos_dev_capability_descriptor *dev_cap, struct libusb_container_id_descriptor **container_id); void LIBUSB_CALL libusb_free_container_id_descriptor( struct libusb_container_id_descriptor *container_id); uint8_t LIBUSB_CALL libusb_get_bus_number(libusb_device *dev); uint8_t LIBUSB_CALL libusb_get_port_number(libusb_device *dev); -int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t* port_numbers, int port_numbers_len); +int LIBUSB_CALL libusb_get_port_numbers(libusb_device *dev, uint8_t *port_numbers, int port_numbers_len); LIBUSB_DEPRECATED_FOR(libusb_get_port_numbers) -int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t* path, uint8_t path_length); +int LIBUSB_CALL libusb_get_port_path(libusb_context *ctx, libusb_device *dev, uint8_t *path, uint8_t path_length); libusb_device * LIBUSB_CALL libusb_get_parent(libusb_device *dev); uint8_t LIBUSB_CALL libusb_get_device_address(libusb_device *dev); int LIBUSB_CALL libusb_get_device_speed(libusb_device *dev); @@ -1362,6 +1415,7 @@ int LIBUSB_CALL libusb_get_max_packet_size(libusb_device *dev, int LIBUSB_CALL libusb_get_max_iso_packet_size(libusb_device *dev, unsigned char endpoint); +int LIBUSB_CALL libusb_wrap_sys_device(libusb_context *ctx, intptr_t sys_dev, libusb_device_handle **dev_handle); int LIBUSB_CALL libusb_open(libusb_device *dev, libusb_device_handle **dev_handle); void LIBUSB_CALL libusb_close(libusb_device_handle *dev_handle); libusb_device * LIBUSB_CALL libusb_get_device(libusb_device_handle *dev_handle); @@ -1436,7 +1490,7 @@ static inline unsigned char *libusb_control_transfer_get_data( static inline struct libusb_control_setup *libusb_control_transfer_get_setup( struct libusb_transfer *transfer) { - return (struct libusb_control_setup *)(void *) transfer->buffer; + return (struct libusb_control_setup *)(void *)transfer->buffer; } /** \ingroup libusb_asyncio @@ -1466,7 +1520,7 @@ static inline void libusb_fill_control_setup(unsigned char *buffer, uint8_t bmRequestType, uint8_t bRequest, uint16_t wValue, uint16_t wIndex, uint16_t wLength) { - struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; setup->bmRequestType = bmRequestType; setup->bRequest = bRequest; setup->wValue = libusb_cpu_to_le16(wValue); @@ -1516,7 +1570,7 @@ static inline void libusb_fill_control_transfer( unsigned char *buffer, libusb_transfer_cb_fn callback, void *user_data, unsigned int timeout) { - struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *) buffer; + struct libusb_control_setup *setup = (struct libusb_control_setup *)(void *)buffer; transfer->dev_handle = dev_handle; transfer->endpoint = 0; transfer->type = LIBUSB_TRANSFER_TYPE_CONTROL; @@ -1655,6 +1709,7 @@ static inline void libusb_set_iso_packet_lengths( struct libusb_transfer *transfer, unsigned int length) { int i; + for (i = 0; i < transfer->num_iso_packets; i++) transfer->iso_packet_desc[i].length = length; } @@ -1869,7 +1924,7 @@ void LIBUSB_CALL libusb_set_pollfd_notifiers(libusb_context *ctx, * Callbacks handles are generated by libusb_hotplug_register_callback() * and can be used to deregister callbacks. Callback handles are unique * per libusb_context and it is safe to call libusb_hotplug_deregister_callback() - * on an already deregisted callback. + * on an already deregistered callback. * * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 * @@ -1881,29 +1936,30 @@ typedef int libusb_hotplug_callback_handle; * * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 * - * Flags for hotplug events */ + * Hotplug events */ typedef enum { - /** Default value when not using any flags. */ - LIBUSB_HOTPLUG_NO_FLAGS = 0, + /** A device has been plugged in and is ready to use */ + LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = (1 << 0), - /** Arm the callback and fire it for all matching currently attached devices. */ - LIBUSB_HOTPLUG_ENUMERATE = 1<<0, -} libusb_hotplug_flag; + /** A device has left and is no longer available. + * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. + * It is safe to call libusb_get_device_descriptor on a device that has left */ + LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = (1 << 1) +} libusb_hotplug_event; /** \ingroup libusb_hotplug * * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 * - * Hotplug events */ + * Hotplug flags */ typedef enum { - /** A device has been plugged in and is ready to use */ - LIBUSB_HOTPLUG_EVENT_DEVICE_ARRIVED = 0x01, + /** Arm the callback and fire it for all matching currently attached devices. */ + LIBUSB_HOTPLUG_ENUMERATE = (1 << 0) +} libusb_hotplug_flag; - /** A device has left and is no longer available. - * It is the user's responsibility to call libusb_close on any handle associated with a disconnected device. - * It is safe to call libusb_get_device_descriptor on a device that has left */ - LIBUSB_HOTPLUG_EVENT_DEVICE_LEFT = 0x02, -} libusb_hotplug_event; +/** \ingroup libusb_hotplug + * Convenience macro when not using any flags */ +#define LIBUSB_HOTPLUG_NO_FLAGS 0 /** \ingroup libusb_hotplug * Wildcard matching for hotplug events */ @@ -1932,9 +1988,7 @@ typedef enum { * returning 1 will cause this callback to be deregistered */ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, - libusb_device *device, - libusb_hotplug_event event, - void *user_data); + libusb_device *device, libusb_hotplug_event event, void *user_data); /** \ingroup libusb_hotplug * Register a hotplug callback function @@ -1959,9 +2013,10 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * Since version 1.0.16, \ref LIBUSB_API_VERSION >= 0x01000102 * * \param[in] ctx context to register this callback with - * \param[in] events bitwise or of events that will trigger this callback. See \ref - * libusb_hotplug_event - * \param[in] flags hotplug callback flags. See \ref libusb_hotplug_flag + * \param[in] events bitwise or of hotplug events that will trigger this callback. + * See \ref libusb_hotplug_event + * \param[in] flags bitwise or of hotplug flags that affect registration. + * See \ref libusb_hotplug_flag * \param[in] vendor_id the vendor id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY * \param[in] product_id the product id to match or \ref LIBUSB_HOTPLUG_MATCH_ANY * \param[in] dev_class the device class to match or \ref LIBUSB_HOTPLUG_MATCH_ANY @@ -1971,13 +2026,10 @@ typedef int (LIBUSB_CALL *libusb_hotplug_callback_fn)(libusb_context *ctx, * \returns LIBUSB_SUCCESS on success LIBUSB_ERROR code on failure */ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, - libusb_hotplug_event events, - libusb_hotplug_flag flags, - int vendor_id, int product_id, - int dev_class, - libusb_hotplug_callback_fn cb_fn, - void *user_data, - libusb_hotplug_callback_handle *callback_handle); + int events, int flags, + int vendor_id, int product_id, int dev_class, + libusb_hotplug_callback_fn cb_fn, void *user_data, + libusb_hotplug_callback_handle *callback_handle); /** \ingroup libusb_hotplug * Deregisters a hotplug callback. @@ -1991,7 +2043,18 @@ int LIBUSB_CALL libusb_hotplug_register_callback(libusb_context *ctx, * \param[in] callback_handle the handle of the callback to deregister */ void LIBUSB_CALL libusb_hotplug_deregister_callback(libusb_context *ctx, - libusb_hotplug_callback_handle callback_handle); + libusb_hotplug_callback_handle callback_handle); + +/** \ingroup libusb_hotplug + * Gets the user_data associated with a hotplug callback. + * + * Since version v1.0.24 \ref LIBUSB_API_VERSION >= 0x01000108 + * + * \param[in] ctx context this callback is registered with + * \param[in] callback_handle the handle of the callback to get the user_data of + */ +void * LIBUSB_CALL libusb_hotplug_get_user_data(libusb_context *ctx, + libusb_hotplug_callback_handle callback_handle); /** \ingroup libusb_lib * Available option values for libusb_set_option(). @@ -2018,7 +2081,7 @@ enum libusb_option { * If libusb was compiled with verbose debug message logging, this function * does nothing: you'll always get messages from all levels. */ - LIBUSB_OPTION_LOG_LEVEL, + LIBUSB_OPTION_LOG_LEVEL = 0, /** Use the UsbDk backend for a specific context, if available. * @@ -2027,12 +2090,23 @@ enum libusb_option { * * Only valid on Windows. */ - LIBUSB_OPTION_USE_USBDK, + LIBUSB_OPTION_USE_USBDK = 1, + + /** Set libusb has weak authority. With this option, libusb will skip + * scan devices in libusb_init. + * + * This option should be set before calling libusb_init(), otherwise + * libusb_init will failed. Normally libusb_wrap_sys_device need set + * this option. + * + * Only valid on Linux-based operating system, such as Android. + */ + LIBUSB_OPTION_WEAK_AUTHORITY = 2 }; int LIBUSB_CALL libusb_set_option(libusb_context *ctx, enum libusb_option option, ...); -#ifdef __cplusplus +#if defined(__cplusplus) } #endif diff --git a/mac/libusb/libusb.pro b/mac/libusb/libusb.pro index a5d543756..07c7d23da 100644 --- a/mac/libusb/libusb.pro +++ b/mac/libusb/libusb.pro @@ -15,7 +15,7 @@ SOURCES = core.c \ strerror.c \ sync.c \ os/darwin_usb.c \ - os/poll_posix.c \ + os/events_posix.c \ os/threads_posix.c HEADERS = hotplug.h \ @@ -24,7 +24,7 @@ HEADERS = hotplug.h \ version.h \ version_nano.h \ os/darwin_usb.h \ - os/poll_posix.h \ + os/events_posix.h \ os/threads_posix.h # We use libusb-1.0.0's hardcoded config.h for Xcode diff --git a/mac/libusb/libusbi.h b/mac/libusb/libusbi.h index 31d6ce98d..491114be9 100644 --- a/mac/libusb/libusbi.h +++ b/mac/libusb/libusbi.h @@ -2,6 +2,9 @@ * Internal header for libusb * Copyright © 2007-2009 Daniel Drake * Copyright © 2001 Johannes Erdfelt + * Copyright © 2019 Nathan Hjelm + * Copyright © 2019-2020 Google LLC. All rights reserved. + * Copyright © 2020 Chris Dickens * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -23,34 +26,70 @@ #include -#include - -#include -#include -#include +#include +#include #include -#ifdef HAVE_POLL_H -#include -#endif -#ifdef HAVE_MISSING_H -#include +#include +#include +#ifdef HAVE_SYS_TIME_H +#include #endif #include "libusb.h" -#include "version.h" -/* Attribute to ensure that a structure member is aligned to a natural - * pointer alignment. Used for os_priv member. */ -#if defined(_MSC_VER) -#if defined(_WIN64) -#define PTR_ALIGNED __declspec(align(8)) +/* Not all C standard library headers define static_assert in assert.h + * Additionally, Visual Studio treats static_assert as a keyword. + */ +#if !defined(__cplusplus) && !defined(static_assert) && !defined(_MSC_VER) +#define static_assert(cond, msg) _Static_assert(cond, msg) +#endif + +#ifdef NDEBUG +#define ASSERT_EQ(expression, value) (void)expression +#define ASSERT_NE(expression, value) (void)expression #else -#define PTR_ALIGNED __declspec(align(4)) +#define ASSERT_EQ(expression, value) assert(expression == value) +#define ASSERT_NE(expression, value) assert(expression != value) +#endif + +#define container_of(ptr, type, member) \ + ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member))) + +#ifndef ARRAYSIZE +#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) #endif -#elif defined(__GNUC__) -#define PTR_ALIGNED __attribute__((aligned(sizeof(void *)))) + +#ifndef CLAMP +#define CLAMP(val, min, max) \ + ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) +#endif + +#ifndef MIN +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef MAX +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#endif + +/* The following is used to silence warnings for unused variables */ +#if defined(UNREFERENCED_PARAMETER) +#define UNUSED(var) UNREFERENCED_PARAMETER(var) #else -#define PTR_ALIGNED +#define UNUSED(var) do { (void)(var); } while(0) +#endif + +/* Macro to align a value up to the next multiple of the size of a pointer */ +#define PTR_ALIGN(v) \ + (((v) + (sizeof(void *) - 1)) & ~(sizeof(void *) - 1)) + +/* Internal abstractions for event handling and thread synchronization */ +#if defined(PLATFORM_POSIX) +#include "os/events_posix.h" +#include "os/threads_posix.h" +#elif defined(PLATFORM_WINDOWS) +#include "os/events_windows.h" +#include "os/threads_windows.h" #endif /* Inside the libusb code, mark all public functions as follows: @@ -66,8 +105,6 @@ extern "C" { #endif -#define DEVICE_DESC_LENGTH 18 - #define USB_MAXENDPOINTS 32 #define USB_MAXINTERFACES 32 #define USB_MAXCONFIG 8 @@ -81,13 +118,6 @@ extern "C" { /* Terminator for log lines */ #define USBI_LOG_LINE_END "\n" -/* The following is used to silence warnings for unused variables */ -#define UNUSED(var) do { (void)(var); } while(0) - -#if !defined(ARRAYSIZE) -#define ARRAYSIZE(array) (sizeof(array) / sizeof(array[0])) -#endif - struct list_head { struct list_head *prev, *next; }; @@ -98,11 +128,14 @@ struct list_head { * member - the list_head element in "type" */ #define list_entry(ptr, type, member) \ - ((type *)((uintptr_t)(ptr) - (uintptr_t)offsetof(type, member))) + container_of(ptr, type, member) #define list_first_entry(ptr, type, member) \ list_entry((ptr)->next, type, member) +#define list_next_entry(ptr, type, member) \ + list_entry((ptr)->member.next, type, member) + /* Get each entry from a list * pos - A structure pointer has a "member" element * head - list head @@ -110,15 +143,24 @@ struct list_head { * type - the type of the first parameter */ #define list_for_each_entry(pos, head, member, type) \ - for (pos = list_entry((head)->next, type, member); \ + for (pos = list_first_entry(head, type, member); \ &pos->member != (head); \ - pos = list_entry(pos->member.next, type, member)) + pos = list_next_entry(pos, type, member)) #define list_for_each_entry_safe(pos, n, head, member, type) \ - for (pos = list_entry((head)->next, type, member), \ - n = list_entry(pos->member.next, type, member); \ + for (pos = list_first_entry(head, type, member), \ + n = list_next_entry(pos, type, member); \ &pos->member != (head); \ - pos = n, n = list_entry(n->member.next, type, member)) + pos = n, n = list_next_entry(n, type, member)) + +/* Helper macros to iterate over a list. The structure pointed + * to by "pos" must have a list_head member named "list". + */ +#define for_each_helper(pos, head, type) \ + list_for_each_entry(pos, head, list, type) + +#define for_each_safe_helper(pos, n, head, type) \ + list_for_each_entry_safe(pos, n, head, list, type) #define list_empty(entry) ((entry)->next == (entry)) @@ -155,8 +197,10 @@ static inline void list_del(struct list_head *entry) static inline void list_cut(struct list_head *list, struct list_head *head) { - if (list_empty(head)) + if (list_empty(head)) { + list_init(list); return; + } list->next = head->next; list->next->prev = list; @@ -166,31 +210,52 @@ static inline void list_cut(struct list_head *list, struct list_head *head) list_init(head); } +static inline void list_splice_front(struct list_head *list, struct list_head *head) +{ + list->next->prev = head; + list->prev->next = head->next; + head->next->prev = list->prev; + head->next = list->next; +} + static inline void *usbi_reallocf(void *ptr, size_t size) { void *ret = realloc(ptr, size); + if (!ret) free(ptr); return ret; } -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *mptr = (ptr); \ - (type *)( (char *)mptr - offsetof(type,member) );}) - -#ifndef CLAMP -#define CLAMP(val, min, max) ((val) < (min) ? (min) : ((val) > (max) ? (max) : (val))) -#endif -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#if !defined(USEC_PER_SEC) +#define USEC_PER_SEC 1000000L #endif -#ifndef MAX -#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#if !defined(NSEC_PER_SEC) +#define NSEC_PER_SEC 1000000000L #endif -#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec != 0 || (ts)->tv_nsec != 0) +#define TIMEVAL_IS_VALID(tv) \ + ((tv)->tv_sec >= 0 && \ + (tv)->tv_usec >= 0 && (tv)->tv_usec < USEC_PER_SEC) + +#define TIMESPEC_IS_SET(ts) ((ts)->tv_sec || (ts)->tv_nsec) +#define TIMESPEC_CLEAR(ts) (ts)->tv_sec = (ts)->tv_nsec = 0 +#define TIMESPEC_CMP(a, b, CMP) \ + (((a)->tv_sec == (b)->tv_sec) \ + ? ((a)->tv_nsec CMP (b)->tv_nsec) \ + : ((a)->tv_sec CMP (b)->tv_sec)) +#define TIMESPEC_SUB(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_nsec = (a)->tv_nsec - (b)->tv_nsec; \ + if ((result)->tv_nsec < 0L) { \ + --(result)->tv_sec; \ + (result)->tv_nsec += NSEC_PER_SEC; \ + } \ + } while (0) -#if defined(_WIN32) || defined(__CYGWIN__) || defined(_WIN32_WCE) +#if defined(PLATFORM_WINDOWS) #define TIMEVAL_TV_SEC_TYPE long #else #define TIMEVAL_TV_SEC_TYPE time_t @@ -201,102 +266,66 @@ static inline void *usbi_reallocf(void *ptr, size_t size) #define TIMESPEC_TO_TIMEVAL(tv, ts) \ do { \ (tv)->tv_sec = (TIMEVAL_TV_SEC_TYPE) (ts)->tv_sec; \ - (tv)->tv_usec = (ts)->tv_nsec / 1000; \ + (tv)->tv_usec = (ts)->tv_nsec / 1000L; \ } while (0) #endif #ifdef ENABLE_LOGGING #if defined(_MSC_VER) && (_MSC_VER < 1900) +#include #define snprintf usbi_snprintf #define vsnprintf usbi_vsnprintf int usbi_snprintf(char *dst, size_t size, const char *format, ...); -int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list ap); +int usbi_vsnprintf(char *dst, size_t size, const char *format, va_list args); #define LIBUSB_PRINTF_WIN32 #endif /* defined(_MSC_VER) && (_MSC_VER < 1900) */ void usbi_log(struct libusb_context *ctx, enum libusb_log_level level, - const char *function, const char *format, ...); - -void usbi_log_v(struct libusb_context *ctx, enum libusb_log_level level, - const char *function, const char *format, va_list args); - -#if !defined(_MSC_VER) || (_MSC_VER >= 1400) - -#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __FUNCTION__, __VA_ARGS__) + const char *function, const char *format, ...) PRINTF_FORMAT(4, 5); -#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__) -#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__) -#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__) -#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) +#define _usbi_log(ctx, level, ...) usbi_log(ctx, level, __func__, __VA_ARGS__) -#else /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */ - -#define LOG_BODY(ctxt, level) \ -{ \ - va_list args; \ - va_start(args, format); \ - usbi_log_v(ctxt, level, "", format, args); \ - va_end(args); \ -} - -static inline void usbi_err(struct libusb_context *ctx, const char *format, ...) - LOG_BODY(ctx, LIBUSB_LOG_LEVEL_ERROR) -static inline void usbi_warn(struct libusb_context *ctx, const char *format, ...) - LOG_BODY(ctx, LIBUSB_LOG_LEVEL_WARNING) -static inline void usbi_info(struct libusb_context *ctx, const char *format, ...) - LOG_BODY(ctx, LIBUSB_LOG_LEVEL_INFO) -static inline void usbi_dbg(const char *format, ...) - LOG_BODY(NULL, LIBUSB_LOG_LEVEL_DEBUG) - -#endif /* !defined(_MSC_VER) || (_MSC_VER >= 1400) */ +#define usbi_err(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_ERROR, __VA_ARGS__) +#define usbi_warn(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_WARNING, __VA_ARGS__) +#define usbi_info(ctx, ...) _usbi_log(ctx, LIBUSB_LOG_LEVEL_INFO, __VA_ARGS__) +#define usbi_dbg(...) _usbi_log(NULL, LIBUSB_LOG_LEVEL_DEBUG, __VA_ARGS__) #else /* ENABLE_LOGGING */ -#define usbi_err(ctx, ...) do { (void)ctx; } while (0) -#define usbi_warn(ctx, ...) do { (void)ctx; } while (0) -#define usbi_info(ctx, ...) do { (void)ctx; } while (0) -#define usbi_dbg(...) do {} while (0) +#define usbi_err(ctx, ...) UNUSED(ctx) +#define usbi_warn(ctx, ...) UNUSED(ctx) +#define usbi_info(ctx, ...) UNUSED(ctx) +#define usbi_dbg(...) do {} while (0) #endif /* ENABLE_LOGGING */ -#define USBI_GET_CONTEXT(ctx) \ - do { \ - if (!(ctx)) \ - (ctx) = usbi_default_context; \ - } while(0) - #define DEVICE_CTX(dev) ((dev)->ctx) #define HANDLE_CTX(handle) (DEVICE_CTX((handle)->dev)) #define TRANSFER_CTX(transfer) (HANDLE_CTX((transfer)->dev_handle)) -#define ITRANSFER_CTX(transfer) \ - (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer))) +#define ITRANSFER_CTX(itransfer) \ + (TRANSFER_CTX(USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer))) #define IS_EPIN(ep) (0 != ((ep) & LIBUSB_ENDPOINT_IN)) #define IS_EPOUT(ep) (!IS_EPIN(ep)) #define IS_XFERIN(xfer) (0 != ((xfer)->endpoint & LIBUSB_ENDPOINT_IN)) #define IS_XFEROUT(xfer) (!IS_XFERIN(xfer)) -/* Internal abstraction for thread synchronization */ -#if defined(THREADS_POSIX) -#include "os/threads_posix.h" -#elif defined(OS_WINDOWS) || defined(OS_WINCE) -#include "os/threads_windows.h" -#endif - -extern struct libusb_context *usbi_default_context; - -/* Forward declaration for use in context (fully defined inside poll abstraction) */ -struct pollfd; - struct libusb_context { #if defined(ENABLE_LOGGING) && !defined(ENABLE_DEBUG_LOGGING) enum libusb_log_level debug; int debug_fixed; + libusb_log_cb log_handler; #endif - /* internal event pipe, used for signalling occurrence of an internal event. */ - int event_pipe[2]; + /* used for signalling occurrence of an internal event. */ + usbi_event_t event; + +#ifdef HAVE_OS_TIMER + /* used for timeout handling, if supported by OS. + * this timer is maintained to trigger on the next pending timeout */ + usbi_timer_t timer; +#endif struct list_head usb_devs; usbi_mutex_t usb_devs_lock; @@ -320,10 +349,12 @@ struct libusb_context { * take this lock first */ usbi_mutex_t flying_transfers_lock; +#if !defined(PLATFORM_WINDOWS) /* user callbacks for pollfd changes */ libusb_pollfd_added_cb fd_added_cb; libusb_pollfd_removed_cb fd_removed_cb; void *fd_cb_user_data; +#endif /* ensures that only one thread is handling events at any one time */ usbi_mutex_t events_lock; @@ -351,11 +382,17 @@ struct libusb_context { * in order to safely close a device. Protected by event_data_lock. */ unsigned int device_close; - /* list and count of poll fds and an array of poll fd structures that is - * (re)allocated as necessary prior to polling. Protected by event_data_lock. */ - struct list_head ipollfds; - struct pollfd *pollfds; - POLL_NFDS_TYPE pollfds_cnt; + /* A list of currently active event sources. Protected by event_data_lock. */ + struct list_head event_sources; + + /* A list of event sources that have been removed since the last time + * event sources were waited on. Protected by event_data_lock. */ + struct list_head removed_event_sources; + + /* A pointer and count to platform-specific data used for monitoring event + * sources. Only accessed during event handling. */ + void *event_data; + unsigned int event_data_cnt; /* A list of pending hotplug messages. Protected by event_data_lock. */ struct list_head hotplug_msgs; @@ -363,48 +400,54 @@ struct libusb_context { /* A list of pending completed transfers. Protected by event_data_lock. */ struct list_head completed_transfers; -#ifdef USBI_TIMERFD_AVAILABLE - /* used for timeout handling, if supported by OS. - * this timerfd is maintained to trigger on the next pending timeout */ - int timerfd; -#endif - struct list_head list; - - PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY]; }; +extern struct libusb_context *usbi_default_context; + +extern struct list_head active_contexts_list; +extern usbi_mutex_static_t active_contexts_lock; + +static inline struct libusb_context *usbi_get_context(struct libusb_context *ctx) +{ + return ctx ? ctx : usbi_default_context; +} + enum usbi_event_flags { - /* The list of pollfds has been modified */ - USBI_EVENT_POLLFDS_MODIFIED = 1 << 0, + /* The list of event sources has been modified */ + USBI_EVENT_EVENT_SOURCES_MODIFIED = 1U << 0, /* The user has interrupted the event handler */ - USBI_EVENT_USER_INTERRUPT = 1 << 1, + USBI_EVENT_USER_INTERRUPT = 1U << 1, /* A hotplug callback deregistration is pending */ - USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1 << 2, -}; + USBI_EVENT_HOTPLUG_CB_DEREGISTERED = 1U << 2, -/* Macros for managing event handling state */ -#define usbi_handling_events(ctx) \ - (usbi_tls_key_get((ctx)->event_handling_key) != NULL) + /* One or more hotplug messages are pending */ + USBI_EVENT_HOTPLUG_MSG_PENDING = 1U << 3, -#define usbi_start_event_handling(ctx) \ - usbi_tls_key_set((ctx)->event_handling_key, ctx) + /* One or more completed transfers are pending */ + USBI_EVENT_TRANSFER_COMPLETED = 1U << 4, -#define usbi_end_event_handling(ctx) \ - usbi_tls_key_set((ctx)->event_handling_key, NULL) + /* A device is in the process of being closed */ + USBI_EVENT_DEVICE_CLOSE = 1U << 5, +}; -/* Update the following macro if new event sources are added */ -#define usbi_pending_events(ctx) \ - ((ctx)->event_flags || (ctx)->device_close \ - || !list_empty(&(ctx)->hotplug_msgs) || !list_empty(&(ctx)->completed_transfers)) +/* Macros for managing event handling state */ +static inline int usbi_handling_events(struct libusb_context *ctx) +{ + return usbi_tls_key_get(ctx->event_handling_key) != NULL; +} -#ifdef USBI_TIMERFD_AVAILABLE -#define usbi_using_timerfd(ctx) ((ctx)->timerfd >= 0) -#else -#define usbi_using_timerfd(ctx) (0) -#endif +static inline void usbi_start_event_handling(struct libusb_context *ctx) +{ + usbi_tls_key_set(ctx->event_handling_key, ctx); +} + +static inline void usbi_end_event_handling(struct libusb_context *ctx) +{ + usbi_tls_key_set(ctx->event_handling_key, NULL); +} struct libusb_device { /* lock protects refcnt, everything else is finalized at initialization @@ -413,12 +456,11 @@ struct libusb_device { int refcnt; struct libusb_context *ctx; + struct libusb_device *parent_dev; uint8_t bus_number; uint8_t port_number; - struct libusb_device* parent_dev; uint8_t device_address; - uint8_t num_configurations; enum libusb_speed speed; struct list_head list; @@ -426,8 +468,6 @@ struct libusb_device { struct libusb_device_descriptor device_descriptor; int attached; - - PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY]; }; struct libusb_device_handle { @@ -438,37 +478,61 @@ struct libusb_device_handle { struct list_head list; struct libusb_device *dev; int auto_detach_kernel_driver; - - PTR_ALIGNED unsigned char os_priv[ZERO_SIZED_ARRAY]; }; -enum { - USBI_CLOCK_MONOTONIC, - USBI_CLOCK_REALTIME -}; +/* Function called by backend during device initialization to convert + * multi-byte fields in the device descriptor to host-endian format. + */ +static inline void usbi_localize_device_descriptor(struct libusb_device_descriptor *desc) +{ + desc->bcdUSB = libusb_le16_to_cpu(desc->bcdUSB); + desc->idVendor = libusb_le16_to_cpu(desc->idVendor); + desc->idProduct = libusb_le16_to_cpu(desc->idProduct); + desc->bcdDevice = libusb_le16_to_cpu(desc->bcdDevice); +} + +#ifdef HAVE_CLOCK_GETTIME +static inline void usbi_get_monotonic_time(struct timespec *tp) +{ + ASSERT_EQ(clock_gettime(CLOCK_MONOTONIC, tp), 0); +} +static inline void usbi_get_real_time(struct timespec *tp) +{ + ASSERT_EQ(clock_gettime(CLOCK_REALTIME, tp), 0); +} +#else +/* If the platform doesn't provide the clock_gettime() function, the backend + * must provide its own clock implementations. Two clock functions are + * required: + * + * usbi_get_monotonic_time(): returns the time since an unspecified starting + * point (usually boot) that is monotonically + * increasing. + * usbi_get_real_time(): returns the time since system epoch. + */ +void usbi_get_monotonic_time(struct timespec *tp); +void usbi_get_real_time(struct timespec *tp); +#endif /* in-memory transfer layout: * - * 1. struct usbi_transfer - * 2. struct libusb_transfer (which includes iso packets) [variable size] - * 3. os private data [variable size] + * 1. os private data + * 2. struct usbi_transfer + * 3. struct libusb_transfer (which includes iso packets) [variable size] * * from a libusb_transfer, you can get the usbi_transfer by rewinding the * appropriate number of bytes. - * the usbi_transfer includes the number of allocated packets, so you can - * determine the size of the transfer and hence the start and length of the - * OS-private data. */ struct usbi_transfer { int num_iso_packets; struct list_head list; struct list_head completed_list; - struct timeval timeout; + struct timespec timeout; int transferred; uint32_t stream_id; - uint8_t state_flags; /* Protected by usbi_transfer->lock */ - uint8_t timeout_flags; /* Protected by the flying_stransfers_lock */ + uint32_t state_flags; /* Protected by usbi_transfer->lock */ + uint32_t timeout_flags; /* Protected by the flying_stransfers_lock */ /* this lock is held during libusb_submit_transfer() and * libusb_cancel_transfer() (allowing the OS backend to prevent duplicate @@ -480,51 +544,124 @@ struct usbi_transfer { * Note paths taking both this and the flying_transfers_lock must * always take the flying_transfers_lock first */ usbi_mutex_t lock; + + void *priv; }; enum usbi_transfer_state_flags { /* Transfer successfully submitted by backend */ - USBI_TRANSFER_IN_FLIGHT = 1 << 0, + USBI_TRANSFER_IN_FLIGHT = 1U << 0, /* Cancellation was requested via libusb_cancel_transfer() */ - USBI_TRANSFER_CANCELLING = 1 << 1, + USBI_TRANSFER_CANCELLING = 1U << 1, /* Operation on the transfer failed because the device disappeared */ - USBI_TRANSFER_DEVICE_DISAPPEARED = 1 << 2, + USBI_TRANSFER_DEVICE_DISAPPEARED = 1U << 2, }; enum usbi_transfer_timeout_flags { /* Set by backend submit_transfer() if the OS handles timeout */ - USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1 << 0, + USBI_TRANSFER_OS_HANDLES_TIMEOUT = 1U << 0, /* The transfer timeout has been handled */ - USBI_TRANSFER_TIMEOUT_HANDLED = 1 << 1, + USBI_TRANSFER_TIMEOUT_HANDLED = 1U << 1, /* The transfer timeout was successfully processed */ - USBI_TRANSFER_TIMED_OUT = 1 << 2, + USBI_TRANSFER_TIMED_OUT = 1U << 2, }; -#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(transfer) \ - ((struct libusb_transfer *)(((unsigned char *)(transfer)) \ - + sizeof(struct usbi_transfer))) -#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ - ((struct usbi_transfer *)(((unsigned char *)(transfer)) \ - - sizeof(struct usbi_transfer))) +#define USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer) \ + ((struct libusb_transfer *) \ + ((unsigned char *)(itransfer) \ + + PTR_ALIGN(sizeof(struct usbi_transfer)))) +#define LIBUSB_TRANSFER_TO_USBI_TRANSFER(transfer) \ + ((struct usbi_transfer *) \ + ((unsigned char *)(transfer) \ + - PTR_ALIGN(sizeof(struct usbi_transfer)))) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif -static inline void *usbi_transfer_get_os_priv(struct usbi_transfer *transfer) -{ - return ((unsigned char *)transfer) + sizeof(struct usbi_transfer) - + sizeof(struct libusb_transfer) - + (transfer->num_iso_packets - * sizeof(struct libusb_iso_packet_descriptor)); -} +/* All standard descriptors have these 2 fields in common */ +struct usbi_descriptor_header { + uint8_t bLength; + uint8_t bDescriptorType; +} LIBUSB_PACKED; + +struct usbi_device_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t bcdUSB; + uint8_t bDeviceClass; + uint8_t bDeviceSubClass; + uint8_t bDeviceProtocol; + uint8_t bMaxPacketSize0; + uint16_t idVendor; + uint16_t idProduct; + uint16_t bcdDevice; + uint8_t iManufacturer; + uint8_t iProduct; + uint8_t iSerialNumber; + uint8_t bNumConfigurations; +} LIBUSB_PACKED; + +struct usbi_configuration_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumInterfaces; + uint8_t bConfigurationValue; + uint8_t iConfiguration; + uint8_t bmAttributes; + uint8_t bMaxPower; +} LIBUSB_PACKED; + +struct usbi_interface_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint8_t bInterfaceNumber; + uint8_t bAlternateSetting; + uint8_t bNumEndpoints; + uint8_t bInterfaceClass; + uint8_t bInterfaceSubClass; + uint8_t bInterfaceProtocol; + uint8_t iInterface; +} LIBUSB_PACKED; + +struct usbi_string_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wData[ZERO_SIZED_ARRAY]; +} LIBUSB_PACKED; + +struct usbi_bos_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + uint16_t wTotalLength; + uint8_t bNumDeviceCaps; +} LIBUSB_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif -/* bus structures */ +union usbi_config_desc_buf { + struct usbi_configuration_descriptor desc; + uint8_t buf[LIBUSB_DT_CONFIG_SIZE]; + uint16_t align; /* Force 2-byte alignment */ +}; -/* All standard descriptors have these 2 fields in common */ -struct usb_descriptor_header { - uint8_t bLength; - uint8_t bDescriptorType; +union usbi_string_desc_buf { + struct usbi_string_descriptor desc; + uint8_t buf[255]; /* Some devices choke on size > 255 */ + uint16_t align; /* Force 2-byte alignment */ +}; + +union usbi_bos_desc_buf { + struct usbi_bos_descriptor desc; + uint8_t buf[LIBUSB_DT_BOS_SIZE]; + uint16_t align; /* Force 2-byte alignment */ }; /* shared data and functions */ @@ -541,39 +678,88 @@ void usbi_handle_disconnect(struct libusb_device_handle *dev_handle); int usbi_handle_transfer_completion(struct usbi_transfer *itransfer, enum libusb_transfer_status status); -int usbi_handle_transfer_cancellation(struct usbi_transfer *transfer); -void usbi_signal_transfer_completion(struct usbi_transfer *transfer); - -int usbi_parse_descriptor(const unsigned char *source, const char *descriptor, - void *dest, int host_endian); -int usbi_device_cache_descriptor(libusb_device *dev); -int usbi_get_config_index_by_value(struct libusb_device *dev, - uint8_t bConfigurationValue, int *idx); - -void usbi_connect_device (struct libusb_device *dev); -void usbi_disconnect_device (struct libusb_device *dev); - -int usbi_signal_event(struct libusb_context *ctx); -int usbi_clear_event(struct libusb_context *ctx); - -/* Internal abstraction for poll (needs struct usbi_transfer on Windows) */ -#if defined(OS_LINUX) || defined(OS_DARWIN) || defined(OS_OPENBSD) || defined(OS_NETBSD) ||\ - defined(OS_HAIKU) || defined(OS_SUNOS) -#include -#include "os/poll_posix.h" -#elif defined(OS_WINDOWS) || defined(OS_WINCE) -#include "os/poll_windows.h" -#endif +int usbi_handle_transfer_cancellation(struct usbi_transfer *itransfer); +void usbi_signal_transfer_completion(struct usbi_transfer *itransfer); -struct usbi_pollfd { - /* must come first */ - struct libusb_pollfd pollfd; +void usbi_connect_device(struct libusb_device *dev); +void usbi_disconnect_device(struct libusb_device *dev); +struct usbi_event_source { + struct usbi_event_source_data { + usbi_os_handle_t os_handle; + short poll_events; + } data; struct list_head list; }; -int usbi_add_pollfd(struct libusb_context *ctx, int fd, short events); -void usbi_remove_pollfd(struct libusb_context *ctx, int fd); +int usbi_add_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle, + short poll_events); +void usbi_remove_event_source(struct libusb_context *ctx, usbi_os_handle_t os_handle); + +/* OS event abstraction */ + +int usbi_create_event(usbi_event_t *event); +void usbi_destroy_event(usbi_event_t *event); +void usbi_signal_event(usbi_event_t *event); +void usbi_clear_event(usbi_event_t *event); + +#ifdef HAVE_OS_TIMER +int usbi_create_timer(usbi_timer_t *timer); +void usbi_destroy_timer(usbi_timer_t *timer); +int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout); +int usbi_disarm_timer(usbi_timer_t *timer); +#endif + +static inline int usbi_using_timer(struct libusb_context *ctx) +{ +#ifdef HAVE_OS_TIMER + return usbi_timer_valid(&ctx->timer); +#else + UNUSED(ctx); + return 0; +#endif +} + +struct usbi_reported_events { + union { + struct { + unsigned int event_triggered:1; +#ifdef HAVE_OS_TIMER + unsigned int timer_triggered:1; +#endif + }; + unsigned int event_bits; + }; + void *event_data; + unsigned int event_data_count; + unsigned int num_ready; +}; + +int usbi_alloc_event_data(struct libusb_context *ctx); +int usbi_wait_for_events(struct libusb_context *ctx, + struct usbi_reported_events *reported_events, int timeout_ms); + +/* accessor functions for structure private data */ + +static inline void *usbi_get_context_priv(struct libusb_context *ctx) +{ + return (unsigned char *)ctx + PTR_ALIGN(sizeof(*ctx)); +} + +static inline void *usbi_get_device_priv(struct libusb_device *dev) +{ + return (unsigned char *)dev + PTR_ALIGN(sizeof(*dev)); +} + +static inline void *usbi_get_device_handle_priv(struct libusb_device_handle *dev_handle) +{ + return (unsigned char *)dev_handle + PTR_ALIGN(sizeof(*dev_handle)); +} + +static inline void *usbi_get_transfer_priv(struct usbi_transfer *itransfer) +{ + return itransfer->priv; +} /* device discovery */ @@ -701,6 +887,34 @@ struct usbi_os_backend { */ void (*hotplug_poll)(void); + /* Wrap a platform-specific device handle for I/O and other USB + * operations. The device handle is preallocated for you. + * + * Your backend should allocate any internal resources required for I/O + * and other operations so that those operations can happen (hopefully) + * without hiccup. This is also a good place to inform libusb that it + * should monitor certain file descriptors related to this device - + * see the usbi_add_event_source() function. + * + * Your backend should also initialize the device structure + * (dev_handle->dev), which is NULL at the beginning of the call. + * + * This function should not generate any bus I/O and should not block. + * + * This function is called when the user attempts to wrap an existing + * platform-specific device handle for a device. + * + * Return: + * - 0 on success + * - LIBUSB_ERROR_ACCESS if the user has insufficient permissions + * - another LIBUSB_ERROR code on other failure + * + * Do not worry about freeing the handle on failed open, the upper layers + * do this for you. + */ + int (*wrap_sys_device)(struct libusb_context *ctx, + struct libusb_device_handle *dev_handle, intptr_t sys_dev); + /* Open a device for I/O and other USB operations. The device handle * is preallocated for you, you can retrieve the device in question * through handle->dev. @@ -709,7 +923,7 @@ struct usbi_os_backend { * and other operations so that those operations can happen (hopefully) * without hiccup. This is also a good place to inform libusb that it * should monitor certain file descriptors related to this device - - * see the usbi_add_pollfd() function. + * see the usbi_add_event_source() function. * * This function should not generate any bus I/O and should not block. * @@ -730,38 +944,14 @@ struct usbi_os_backend { /* Close a device such that the handle cannot be used again. Your backend * should destroy any resources that were allocated in the open path. - * This may also be a good place to call usbi_remove_pollfd() to inform - * libusb of any file descriptors associated with this device that should - * no longer be monitored. + * This may also be a good place to call usbi_remove_event_source() to + * inform libusb of any event sources associated with this device that + * should no longer be monitored. * * This function is called when the user closes a device handle. */ void (*close)(struct libusb_device_handle *dev_handle); - /* Retrieve the device descriptor from a device. - * - * The descriptor should be retrieved from memory, NOT via bus I/O to the - * device. This means that you may have to cache it in a private structure - * during get_device_list enumeration. Alternatively, you may be able - * to retrieve it from a kernel interface (some Linux setups can do this) - * still without generating bus I/O. - * - * This function is expected to write DEVICE_DESC_LENGTH (18) bytes into - * buffer, which is guaranteed to be big enough. - * - * This function is called when sanity-checking a device before adding - * it to the list of discovered devices, and also when the user requests - * to read the device descriptor. - * - * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". - * - * Return 0 on success or a LIBUSB_ERROR code on failure. - */ - int (*get_device_descriptor)(struct libusb_device *device, - unsigned char *buffer, int *host_endian); - /* Get the ACTIVE configuration descriptor for a device. * * The descriptor should be retrieved from memory, NOT via bus I/O to the @@ -774,8 +964,7 @@ struct usbi_os_backend { * return an error code. * * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". + * (LE). * * Return: * - 0 on success @@ -783,7 +972,7 @@ struct usbi_os_backend { * - another LIBUSB_ERROR code on other failure */ int (*get_active_config_descriptor)(struct libusb_device *device, - unsigned char *buffer, size_t len, int *host_endian); + void *buffer, size_t len); /* Get a specific configuration descriptor for a device. * @@ -801,14 +990,12 @@ struct usbi_os_backend { * return an error code. * * This function is expected to return the descriptor in bus-endian format - * (LE). If it returns the multi-byte values in host-endian format, - * set the host_endian output parameter to "1". + * (LE). * * Return the length read on success or a LIBUSB_ERROR code on failure. */ int (*get_config_descriptor)(struct libusb_device *device, - uint8_t config_index, unsigned char *buffer, size_t len, - int *host_endian); + uint8_t config_index, void *buffer, size_t len); /* Like get_config_descriptor but then by bConfigurationValue instead * of by index. @@ -823,8 +1010,7 @@ struct usbi_os_backend { * or a LIBUSB_ERROR code on failure. */ int (*get_config_descriptor_by_value)(struct libusb_device *device, - uint8_t bConfigurationValue, unsigned char **buffer, - int *host_endian); + uint8_t bConfigurationValue, void **buffer); /* Get the bConfigurationValue for the active configuration for a device. * Optional. This should only be implemented if you can retrieve it from @@ -843,7 +1029,7 @@ struct usbi_os_backend { * blocking * - another LIBUSB_ERROR code on other failure. */ - int (*get_configuration)(struct libusb_device_handle *dev_handle, int *config); + int (*get_configuration)(struct libusb_device_handle *dev_handle, uint8_t *config); /* Set the active configuration for a device. * @@ -879,7 +1065,7 @@ struct usbi_os_backend { * was opened * - another LIBUSB_ERROR code on other failure */ - int (*claim_interface)(struct libusb_device_handle *dev_handle, int interface_number); + int (*claim_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); /* Release a previously claimed interface. * @@ -896,7 +1082,7 @@ struct usbi_os_backend { * was opened * - another LIBUSB_ERROR code on other failure */ - int (*release_interface)(struct libusb_device_handle *dev_handle, int interface_number); + int (*release_interface)(struct libusb_device_handle *dev_handle, uint8_t interface_number); /* Set the alternate setting for an interface. * @@ -913,7 +1099,7 @@ struct usbi_os_backend { * - another LIBUSB_ERROR code on other failure */ int (*set_interface_altsetting)(struct libusb_device_handle *dev_handle, - int interface_number, int altsetting); + uint8_t interface_number, uint8_t altsetting); /* Clear a halt/stall condition on an endpoint. * @@ -929,13 +1115,13 @@ struct usbi_os_backend { int (*clear_halt)(struct libusb_device_handle *dev_handle, unsigned char endpoint); - /* Perform a USB port reset to reinitialize a device. + /* Perform a USB port reset to reinitialize a device. Optional. * * If possible, the device handle should still be usable after the reset * completes, assuming that the device descriptors did not change during * reset and all previous interface state can be restored. * - * If something changes, or you cannot easily locate/verify the resetted + * If something changes, or you cannot easily locate/verify the reset * device, return LIBUSB_ERROR_NOT_FOUND. This prompts the application * to close the old handle and re-enumerate the device. * @@ -958,12 +1144,11 @@ struct usbi_os_backend { /* Allocate persistent DMA memory for the given device, suitable for * zerocopy. May return NULL on failure. Optional to implement. */ - unsigned char *(*dev_mem_alloc)(struct libusb_device_handle *handle, - size_t len); + void *(*dev_mem_alloc)(struct libusb_device_handle *handle, size_t len); /* Free memory allocated by dev_mem_alloc. */ - int (*dev_mem_free)(struct libusb_device_handle *handle, - unsigned char *buffer, size_t len); + int (*dev_mem_free)(struct libusb_device_handle *handle, void *buffer, + size_t len); /* Determine if a kernel driver is active on an interface. Optional. * @@ -978,7 +1163,7 @@ struct usbi_os_backend { * - another LIBUSB_ERROR code on other failure */ int (*kernel_driver_active)(struct libusb_device_handle *dev_handle, - int interface_number); + uint8_t interface_number); /* Detach a kernel driver from an interface. Optional. * @@ -994,7 +1179,7 @@ struct usbi_os_backend { * - another LIBUSB_ERROR code on other failure */ int (*detach_kernel_driver)(struct libusb_device_handle *dev_handle, - int interface_number); + uint8_t interface_number); /* Attach a kernel driver to an interface. Optional. * @@ -1011,7 +1196,7 @@ struct usbi_os_backend { * - another LIBUSB_ERROR code on other failure */ int (*attach_kernel_driver)(struct libusb_device_handle *dev_handle, - int interface_number); + uint8_t interface_number); /* Destroy a device. Optional. * @@ -1056,21 +1241,22 @@ struct usbi_os_backend { */ void (*clear_transfer_priv)(struct usbi_transfer *itransfer); - /* Handle any pending events on file descriptors. Optional. + /* Handle any pending events on event sources. Optional. * - * Provide this function when file descriptors directly indicate device - * or transfer activity. If your backend does not have such file descriptors, + * Provide this function when event sources directly indicate device + * or transfer activity. If your backend does not have such event sources, * implement the handle_transfer_completion function below. * * This involves monitoring any active transfers and processing their * completion or cancellation. * - * The function is passed an array of pollfd structures (size nfds) - * as a result of the poll() system call. The num_ready parameter - * indicates the number of file descriptors that have reported events - * (i.e. the poll() return value). This should be enough information - * for you to determine which actions need to be taken on the currently - * active transfers. + * The function is passed a pointer that represents platform-specific + * data for monitoring event sources (size count). This data is to be + * (re)allocated as necessary when event sources are modified. + * The num_ready parameter indicates the number of event sources that + * have reported events. This should be enough information for you to + * determine which actions need to be taken on the currently active + * transfers. * * For any cancelled transfers, call usbi_handle_transfer_cancellation(). * For completed transfers, call usbi_handle_transfer_completion(). @@ -1089,13 +1275,13 @@ struct usbi_os_backend { * Return 0 on success, or a LIBUSB_ERROR code on failure. */ int (*handle_events)(struct libusb_context *ctx, - struct pollfd *fds, POLL_NFDS_TYPE nfds, int num_ready); + void *event_data, unsigned int count, unsigned int num_ready); /* Handle transfer completion. Optional. * - * Provide this function when there are no file descriptors available - * that directly indicate device or transfer activity. If your backend does - * have such file descriptors, implement the handle_events function above. + * Provide this function when there are no event sources available that + * directly indicate device or transfer activity. If your backend does + * have such event sources, implement the handle_events function above. * * Your backend must tell the library when a transfer has completed by * calling usbi_signal_transfer_completion(). You should store any private @@ -1116,47 +1302,68 @@ struct usbi_os_backend { */ int (*handle_transfer_completion)(struct usbi_transfer *itransfer); - /* Get time from specified clock. At least two clocks must be implemented - by the backend: USBI_CLOCK_REALTIME, and USBI_CLOCK_MONOTONIC. - - Description of clocks: - USBI_CLOCK_REALTIME : clock returns time since system epoch. - USBI_CLOCK_MONOTONIC: clock returns time since unspecified start - time (usually boot). - */ - int (*clock_gettime)(int clkid, struct timespec *tp); - -#ifdef USBI_TIMERFD_AVAILABLE - /* clock ID of the clock that should be used for timerfd */ - clockid_t (*get_timerfd_clockid)(void); -#endif - /* Number of bytes to reserve for per-context private backend data. - * This private data area is accessible through the "os_priv" field of - * struct libusb_context. */ + * This private data area is accessible by calling + * usbi_get_context_priv() on the libusb_context instance. + */ size_t context_priv_size; /* Number of bytes to reserve for per-device private backend data. - * This private data area is accessible through the "os_priv" field of - * struct libusb_device. */ + * This private data area is accessible by calling + * usbi_get_device_priv() on the libusb_device instance. + */ size_t device_priv_size; /* Number of bytes to reserve for per-handle private backend data. - * This private data area is accessible through the "os_priv" field of - * struct libusb_device. */ + * This private data area is accessible by calling + * usbi_get_device_handle_priv() on the libusb_device_handle instance. + */ size_t device_handle_priv_size; /* Number of bytes to reserve for per-transfer private backend data. * This private data area is accessible by calling - * usbi_transfer_get_os_priv() on the appropriate usbi_transfer instance. + * usbi_get_transfer_priv() on the usbi_transfer instance. */ size_t transfer_priv_size; }; extern const struct usbi_os_backend usbi_backend; -extern struct list_head active_contexts_list; -extern usbi_mutex_static_t active_contexts_lock; +#define for_each_context(c) \ + for_each_helper(c, &active_contexts_list, struct libusb_context) + +#define for_each_device(ctx, d) \ + for_each_helper(d, &(ctx)->usb_devs, struct libusb_device) + +#define for_each_device_safe(ctx, d, n) \ + for_each_safe_helper(d, n, &(ctx)->usb_devs, struct libusb_device) + +#define for_each_open_device(ctx, h) \ + for_each_helper(h, &(ctx)->open_devs, struct libusb_device_handle) + +#define __for_each_transfer(list, t) \ + for_each_helper(t, (list), struct usbi_transfer) + +#define for_each_transfer(ctx, t) \ + __for_each_transfer(&(ctx)->flying_transfers, t) + +#define __for_each_transfer_safe(list, t, n) \ + for_each_safe_helper(t, n, (list), struct usbi_transfer) + +#define for_each_transfer_safe(ctx, t, n) \ + __for_each_transfer_safe(&(ctx)->flying_transfers, t, n) + +#define __for_each_completed_transfer_safe(list, t, n) \ + list_for_each_entry_safe(t, n, (list), completed_list, struct usbi_transfer) + +#define for_each_event_source(ctx, e) \ + for_each_helper(e, &(ctx)->event_sources, struct usbi_event_source) + +#define for_each_removed_event_source(ctx, e) \ + for_each_helper(e, &(ctx)->removed_event_sources, struct usbi_event_source) + +#define for_each_removed_event_source_safe(ctx, e, n) \ + for_each_safe_helper(e, n, &(ctx)->removed_event_sources, struct usbi_event_source) #ifdef __cplusplus } diff --git a/mac/libusb/os/darwin_usb.c b/mac/libusb/os/darwin_usb.c index 35ea1c321..e415589d6 100644 --- a/mac/libusb/os/darwin_usb.c +++ b/mac/libusb/os/darwin_usb.c @@ -1,7 +1,8 @@ /* -*- Mode: C; indent-tabs-mode:nil -*- */ /* * darwin backend for libusb 1.0 - * Copyright © 2008-2017 Nathan Hjelm + * Copyright © 2008-2020 Nathan Hjelm + * Copyright © 2019-2020 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -18,10 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "config.h" +#include +#include #include #include -#include #include #include #include @@ -45,66 +46,49 @@ #include #endif -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 -/* Apple deprecated the darwin atomics in 10.12 in favor of C11 atomics */ -#include -#define libusb_darwin_atomic_fetch_add(x, y) atomic_fetch_add(x, y) - -_Atomic int32_t initCount = ATOMIC_VAR_INIT(0); -#else -/* use darwin atomics if the target is older than 10.12 */ -#include - -/* OSAtomicAdd32Barrier returns the new value */ -#define libusb_darwin_atomic_fetch_add(x, y) (OSAtomicAdd32Barrier(y, x) - y) - -static volatile int32_t initCount = 0; - -#endif - -/* On 10.12 and later, use newly available clock_*() functions */ -#if MAC_OS_X_VERSION_MIN_REQUIRED >= 101200 -#define OSX_USE_CLOCK_GETTIME 1 -#else -#define OSX_USE_CLOCK_GETTIME 0 -#endif - #include "darwin_usb.h" +static pthread_mutex_t libusb_darwin_init_mutex = PTHREAD_MUTEX_INITIALIZER; +static int init_count = 0; + /* async event thread */ static pthread_mutex_t libusb_darwin_at_mutex = PTHREAD_MUTEX_INITIALIZER; static pthread_cond_t libusb_darwin_at_cond = PTHREAD_COND_INITIALIZER; -static pthread_once_t darwin_init_once = PTHREAD_ONCE_INIT; - -#if !OSX_USE_CLOCK_GETTIME +#if !defined(HAVE_CLOCK_GETTIME) static clock_serv_t clock_realtime; static clock_serv_t clock_monotonic; #endif +#define LIBUSB_DARWIN_STARTUP_FAILURE ((CFRunLoopRef) -1) + static CFRunLoopRef libusb_darwin_acfl = NULL; /* event cf loop */ static CFRunLoopSourceRef libusb_darwin_acfls = NULL; /* shutdown signal for event cf loop */ static usbi_mutex_t darwin_cached_devices_lock = PTHREAD_MUTEX_INITIALIZER; -static struct list_head darwin_cached_devices = {&darwin_cached_devices, &darwin_cached_devices}; -static const char *darwin_device_class = kIOUSBDeviceClassName; +static struct list_head darwin_cached_devices; +static const char *darwin_device_class = "IOUSBDevice"; -#define DARWIN_CACHED_DEVICE(a) ((struct darwin_cached_device *) (((struct darwin_device_priv *)((a)->os_priv))->dev)) +#define DARWIN_CACHED_DEVICE(a) (((struct darwin_device_priv *)usbi_get_device_priv((a)))->dev) /* async event thread */ static pthread_t libusb_darwin_at; -static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian); -static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface); -static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface); +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len); +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface); +static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface); static int darwin_reset_device(struct libusb_device_handle *dev_handle); static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0); -static int darwin_scan_devices(struct libusb_context *ctx); -static int process_new_device (struct libusb_context *ctx, io_service_t service); +static enum libusb_error darwin_scan_devices(struct libusb_context *ctx); +static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device, + UInt64 old_session_id); + +static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out, + UInt64 *old_session_id); #if defined(ENABLE_LOGGING) -static const char *darwin_error_str (int result) { +static const char *darwin_error_str (IOReturn result) { static char string_buffer[50]; switch (result) { case kIOReturnSuccess: @@ -137,6 +121,8 @@ static const char *darwin_error_str (int result) { return "out of resources"; case kIOUSBHighSpeedSplitError: return "high speed split error"; + case kIOUSBUnknownPipeErr: + return "pipe ref not recognized"; default: snprintf(string_buffer, sizeof(string_buffer), "unknown error (0x%x)", result); return string_buffer; @@ -144,7 +130,7 @@ static const char *darwin_error_str (int result) { } #endif -static int darwin_to_libusb (int result) { +static enum libusb_error darwin_to_libusb (IOReturn result) { switch (result) { case kIOReturnUnderrun: case kIOReturnSuccess: @@ -164,6 +150,7 @@ static int darwin_to_libusb (int result) { case kIOReturnAborted: case kIOReturnError: case kIOUSBNoAsyncPortErr: + case kIOUSBUnknownPipeErr: default: return LIBUSB_ERROR_OTHER; } @@ -176,7 +163,10 @@ static void darwin_deref_cached_device(struct darwin_cached_device *cached_dev) if (0 == cached_dev->refcount) { list_del(&cached_dev->list); - (*(cached_dev->device))->Release(cached_dev->device); + if (cached_dev->device) { + (*(cached_dev->device))->Release(cached_dev->device); + cached_dev->device = NULL; + } free (cached_dev); } } @@ -186,19 +176,19 @@ static void darwin_ref_cached_device(struct darwin_cached_device *cached_dev) { } static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, uint8_t *pipep, uint8_t *ifcp, struct darwin_interface **interface_out) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); /* current interface */ struct darwin_interface *cInterface; - int8_t i, iface; + uint8_t i, iface; usbi_dbg ("converting ep address 0x%02x to pipeRef and interface", ep); for (iface = 0 ; iface < USB_MAXINTERFACES ; iface++) { cInterface = &priv->interfaces[iface]; - if (dev_handle->claimed_interfaces & (1 << iface)) { + if (dev_handle->claimed_interfaces & (1U << iface)) { for (i = 0 ; i < cInterface->num_endpoints ; i++) { if (cInterface->endpoint_addrs[i] == ep) { *pipep = i + 1; @@ -210,7 +200,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui *interface_out = cInterface; usbi_dbg ("pipe %d on interface %d matches", *pipep, iface); - return 0; + return LIBUSB_SUCCESS; } } } @@ -222,7 +212,7 @@ static int ep_to_pipeRef(struct libusb_device_handle *dev_handle, uint8_t ep, ui return LIBUSB_ERROR_NOT_FOUND; } -static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { +static IOReturn usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 location) { CFMutableDictionaryRef matchingDict = IOServiceMatching(darwin_device_class); if (!matchingDict) @@ -254,24 +244,25 @@ static int usb_setup_device_iterator (io_iterator_t *deviceIterator, UInt32 loca } /* Returns 1 on success, 0 on failure. */ -static int get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { +static bool get_ioregistry_value_number (io_service_t service, CFStringRef property, CFNumberType type, void *p) { CFTypeRef cfNumber = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); - int ret = 0; + Boolean success = 0; if (cfNumber) { if (CFGetTypeID(cfNumber) == CFNumberGetTypeID()) { - ret = CFNumberGetValue(cfNumber, type, p); + success = CFNumberGetValue(cfNumber, type, p); } CFRelease (cfNumber); } - return ret; + return (success != 0); } -static int get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) { +/* Returns 1 on success, 0 on failure. */ +static bool get_ioregistry_value_data (io_service_t service, CFStringRef property, ssize_t size, void *p) { CFTypeRef cfData = IORegistryEntryCreateCFProperty (service, property, kCFAllocatorDefault, 0); - int ret = 0; + bool success = false; if (cfData) { if (CFGetTypeID (cfData) == CFDataGetTypeID ()) { @@ -281,28 +272,42 @@ static int get_ioregistry_value_data (io_service_t service, CFStringRef property } CFDataGetBytes (cfData, CFRangeMake(0, size), p); - ret = 1; + success = true; } CFRelease (cfData); } - return ret; + return success; } static usb_device_t **darwin_device_from_service (io_service_t service) { io_cf_plugin_ref_t *plugInInterface = NULL; usb_device_t **device; - kern_return_t result; + IOReturn kresult; SInt32 score; + const int max_retries = 5; + + /* The IOCreatePlugInInterfaceForService function might consistently return + an "out of resources" error with certain USB devices the first time we run + it. The reason is still unclear, but retrying fixes the problem */ + for (int count = 0; count < max_retries; count++) { + kresult = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, + kIOCFPlugInInterfaceID, &plugInInterface, + &score); + if (kIOReturnSuccess == kresult && plugInInterface) { + break; + } - result = IOCreatePlugInInterfaceForService(service, kIOUSBDeviceUserClientTypeID, - kIOCFPlugInInterfaceID, &plugInInterface, - &score); + usbi_dbg ("set up plugin for service retry: %s", darwin_error_str (kresult)); - if (kIOReturnSuccess != result || !plugInInterface) { - usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (result)); + /* sleep for a little while before trying again */ + nanosleep(&(struct timespec){.tv_sec = 0, .tv_nsec = 1000}, NULL); + } + + if (kIOReturnSuccess != kresult || !plugInInterface) { + usbi_dbg ("could not set up plugin for service: %s", darwin_error_str (kresult)); return NULL; } @@ -316,15 +321,28 @@ static usb_device_t **darwin_device_from_service (io_service_t service) static void darwin_devices_attached (void *ptr, io_iterator_t add_devices) { UNUSED(ptr); + struct darwin_cached_device *cached_device; + UInt64 old_session_id; struct libusb_context *ctx; io_service_t service; + int ret; usbi_mutex_lock(&active_contexts_lock); while ((service = IOIteratorNext(add_devices))) { + ret = darwin_get_cached_device (service, &cached_device, &old_session_id); + if (ret < 0 || !cached_device->can_enumerate) { + continue; + } + /* add this device to each active context's device list */ - list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { - process_new_device (ctx, service); + for_each_context(ctx) { + process_new_device (ctx, cached_device, old_session_id); + } + + if (cached_device->in_reenumerate) { + usbi_dbg ("cached device in reset state. reset complete..."); + cached_device->in_reenumerate = false; } IOObjectRelease(service); @@ -346,6 +364,8 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { usbi_mutex_lock(&active_contexts_lock); while ((device = IOIteratorNext (rem_devices)) != 0) { + bool is_reenumerating = false; + /* get the location from the i/o registry */ ret = get_ioregistry_value_number (device, CFSTR("sessionID"), kCFNumberSInt64Type, &session); IOObjectRelease (device); @@ -357,13 +377,32 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { usbi_mutex_lock(&darwin_cached_devices_lock); list_for_each_entry(old_device, &darwin_cached_devices, list, struct darwin_cached_device) { if (old_device->session == session) { - darwin_deref_cached_device (old_device); + if (old_device->in_reenumerate) { + /* device is re-enumerating. do not dereference the device at this time. libusb_reset_device() + * will deref if needed. */ + usbi_dbg ("detected device detached due to re-enumeration"); + + /* the device object is no longer usable so go ahead and release it */ + if (old_device->device) { + (*(old_device->device))->Release(old_device->device); + old_device->device = NULL; + } + + is_reenumerating = true; + } else { + darwin_deref_cached_device (old_device); + } + break; } } + usbi_mutex_unlock(&darwin_cached_devices_lock); + if (is_reenumerating) { + continue; + } - list_for_each_entry(ctx, &active_contexts_list, list, struct libusb_context) { + for_each_context(ctx) { usbi_dbg ("notifying context %p of device disconnect", ctx); dev = usbi_get_device_by_session_id(ctx, (unsigned long) session); @@ -381,11 +420,11 @@ static void darwin_devices_detached (void *ptr, io_iterator_t rem_devices) { static void darwin_hotplug_poll (void) { - /* not sure if 5 seconds will be too long/short but it should work ok */ - mach_timespec_t timeout = {.tv_sec = 5, .tv_nsec = 0}; + /* not sure if 1 ms will be too long/short but it should work ok */ + mach_timespec_t timeout = {.tv_sec = 0, .tv_nsec = 1000000ul}; - /* since a kernel thread may nodify the IOInterators used for - * hotplug notidication we can't just clear the iterators. + /* since a kernel thread may notify the IOIterators used for + * hotplug notification we can't just clear the iterators. * instead just wait until all IOService providers are quiet */ (void) IOKitWaitQuiet (kIOMasterPortDefault, &timeout); } @@ -397,10 +436,20 @@ static void darwin_clear_iterator (io_iterator_t iter) { IOObjectRelease (device); } +static void darwin_fail_startup(void) { + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfl = LIBUSB_DARWIN_STARTUP_FAILURE; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + pthread_exit (NULL); +} + static void *darwin_event_thread_main (void *arg0) { IOReturn kresult; struct libusb_context *ctx = (struct libusb_context *)arg0; CFRunLoopRef runloop; + CFRunLoopSourceRef libusb_shutdown_cfsource; + CFRunLoopSourceContext libusb_shutdown_cfsourcectx; #if MAC_OS_X_VERSION_MIN_REQUIRED >= 1060 /* Set this thread's name, so it can be seen in the debugger @@ -419,7 +468,6 @@ static void *darwin_event_thread_main (void *arg0) { #endif /* hotplug (device arrival/removal) sources */ - CFRunLoopSourceContext libusb_shutdown_cfsourcectx; CFRunLoopSourceRef libusb_notification_cfsource; io_notification_port_t libusb_notification_port; io_iterator_t libusb_rem_device_iterator; @@ -434,8 +482,8 @@ static void *darwin_event_thread_main (void *arg0) { memset(&libusb_shutdown_cfsourcectx, 0, sizeof(libusb_shutdown_cfsourcectx)); libusb_shutdown_cfsourcectx.info = runloop; libusb_shutdown_cfsourcectx.perform = (void (*)(void *))CFRunLoopStop; - libusb_darwin_acfls = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx); - CFRunLoopAddSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode); + libusb_shutdown_cfsource = CFRunLoopSourceCreate(NULL, 0, &libusb_shutdown_cfsourcectx); + CFRunLoopAddSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); /* add the notification port to the run loop */ libusb_notification_port = IONotificationPortCreate (kIOMasterPortDefault); @@ -450,8 +498,9 @@ static void *darwin_event_thread_main (void *arg0) { if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); - - pthread_exit (NULL); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); } /* create notifications for attached devices */ @@ -462,8 +511,9 @@ static void *darwin_event_thread_main (void *arg0) { if (kresult != kIOReturnSuccess) { usbi_err (ctx, "could not add hotplug event source: %s", darwin_error_str (kresult)); - - pthread_exit (NULL); + CFRelease (libusb_shutdown_cfsource); + CFRelease (runloop); + darwin_fail_startup (); } /* arm notifiers */ @@ -475,6 +525,7 @@ static void *darwin_event_thread_main (void *arg0) { /* signal the main thread that the hotplug runloop has been created. */ pthread_mutex_lock (&libusb_darwin_at_mutex); libusb_darwin_acfl = runloop; + libusb_darwin_acfls = libusb_shutdown_cfsource; pthread_cond_signal (&libusb_darwin_at_cond); pthread_mutex_unlock (&libusb_darwin_at_mutex); @@ -483,11 +534,18 @@ static void *darwin_event_thread_main (void *arg0) { usbi_dbg ("darwin event thread exiting"); + /* signal the main thread that the hotplug runloop has finished. */ + pthread_mutex_lock (&libusb_darwin_at_mutex); + libusb_darwin_acfls = NULL; + libusb_darwin_acfl = NULL; + pthread_cond_signal (&libusb_darwin_at_cond); + pthread_mutex_unlock (&libusb_darwin_at_mutex); + /* remove the notification cfsource */ CFRunLoopRemoveSource(runloop, libusb_notification_cfsource, kCFRunLoopDefaultMode); /* remove the shutdown cfsource */ - CFRunLoopRemoveSource(runloop, libusb_darwin_acfls, kCFRunLoopDefaultMode); + CFRunLoopRemoveSource(runloop, libusb_shutdown_cfsource, kCFRunLoopDefaultMode); /* delete notification port */ IONotificationPortDestroy (libusb_notification_port); @@ -496,104 +554,116 @@ static void *darwin_event_thread_main (void *arg0) { IOObjectRelease (libusb_rem_device_iterator); IOObjectRelease (libusb_add_device_iterator); - CFRelease (libusb_darwin_acfls); + CFRelease (libusb_shutdown_cfsource); CFRelease (runloop); - libusb_darwin_acfls = NULL; - libusb_darwin_acfl = NULL; - pthread_exit (NULL); } /* cleanup function to destroy cached devices */ -static void __attribute__((destructor)) _darwin_finalize(void) { +static void darwin_cleanup_devices(void) { struct darwin_cached_device *dev, *next; - usbi_mutex_lock(&darwin_cached_devices_lock); list_for_each_entry_safe(dev, next, &darwin_cached_devices, list, struct darwin_cached_device) { darwin_deref_cached_device(dev); } - usbi_mutex_unlock(&darwin_cached_devices_lock); -} - -static void darwin_check_version (void) { - /* adjust for changes in the USB stack in xnu 15 */ - int sysctl_args[] = {CTL_KERN, KERN_OSRELEASE}; - long version; - char version_string[256] = {'\0',}; - size_t length = 256; - sysctl(sysctl_args, 2, version_string, &length, NULL, 0); - - errno = 0; - version = strtol (version_string, NULL, 10); - if (0 == errno && version >= 15) { - darwin_device_class = "IOUSBHostDevice"; - } + darwin_cached_devices.prev = darwin_cached_devices.next = NULL; } static int darwin_init(struct libusb_context *ctx) { + bool first_init; int rc; - rc = pthread_once (&darwin_init_once, darwin_check_version); - if (rc) { - return LIBUSB_ERROR_OTHER; - } - - rc = darwin_scan_devices (ctx); - if (LIBUSB_SUCCESS != rc) { - return rc; - } + pthread_mutex_lock (&libusb_darwin_init_mutex); - if (libusb_darwin_atomic_fetch_add (&initCount, 1) == 0) { -#if !OSX_USE_CLOCK_GETTIME - /* create the clocks that will be used if clock_gettime() is not available */ - host_name_port_t host_self; + first_init = (1 == ++init_count); - host_self = mach_host_self(); - host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); - host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); - mach_port_deallocate(mach_task_self(), host_self); + do { + if (first_init) { + assert (NULL == darwin_cached_devices.next); + list_init (&darwin_cached_devices); + +#if !defined(HAVE_CLOCK_GETTIME) + /* create the clocks that will be used if clock_gettime() is not available */ + host_name_port_t host_self; + + host_self = mach_host_self(); + host_get_clock_service(host_self, CALENDAR_CLOCK, &clock_realtime); + host_get_clock_service(host_self, SYSTEM_CLOCK, &clock_monotonic); + mach_port_deallocate(mach_task_self(), host_self); #endif + } - pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); + rc = darwin_scan_devices (ctx); + if (LIBUSB_SUCCESS != rc) + break; - pthread_mutex_lock (&libusb_darwin_at_mutex); - while (!libusb_darwin_acfl) - pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); - pthread_mutex_unlock (&libusb_darwin_at_mutex); + if (first_init) { + rc = pthread_create (&libusb_darwin_at, NULL, darwin_event_thread_main, ctx); + if (0 != rc) { + usbi_err (ctx, "could not create event thread, error %d", rc); + rc = LIBUSB_ERROR_OTHER; + break; + } + + pthread_mutex_lock (&libusb_darwin_at_mutex); + while (!libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + if (libusb_darwin_acfl == LIBUSB_DARWIN_STARTUP_FAILURE) { + libusb_darwin_acfl = NULL; + rc = LIBUSB_ERROR_OTHER; + } + pthread_mutex_unlock (&libusb_darwin_at_mutex); + + if (0 != rc) + pthread_join (libusb_darwin_at, NULL); + } + } while (0); + + if (LIBUSB_SUCCESS != rc) { + if (first_init) { + darwin_cleanup_devices (); +#if !defined(HAVE_CLOCK_GETTIME) + mach_port_deallocate(mach_task_self(), clock_realtime); + mach_port_deallocate(mach_task_self(), clock_monotonic); +#endif + } + --init_count; } + pthread_mutex_unlock (&libusb_darwin_init_mutex); + return rc; } static void darwin_exit (struct libusb_context *ctx) { UNUSED(ctx); - if (libusb_darwin_atomic_fetch_add (&initCount, -1) == 1) { -#if !OSX_USE_CLOCK_GETTIME - mach_port_deallocate(mach_task_self(), clock_realtime); - mach_port_deallocate(mach_task_self(), clock_monotonic); -#endif + pthread_mutex_lock (&libusb_darwin_init_mutex); + + if (0 == --init_count) { /* stop the event runloop and wait for the thread to terminate. */ - CFRunLoopSourceSignal(libusb_darwin_acfls); + pthread_mutex_lock (&libusb_darwin_at_mutex); + CFRunLoopSourceSignal (libusb_darwin_acfls); CFRunLoopWakeUp (libusb_darwin_acfl); + while (libusb_darwin_acfl) + pthread_cond_wait (&libusb_darwin_at_cond, &libusb_darwin_at_mutex); + pthread_mutex_unlock (&libusb_darwin_at_mutex); pthread_join (libusb_darwin_at, NULL); - } -} -static int darwin_get_device_descriptor(struct libusb_device *dev, unsigned char *buffer, int *host_endian) { - struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); - - /* return cached copy */ - memmove (buffer, &(priv->dev_descriptor), DEVICE_DESC_LENGTH); + darwin_cleanup_devices (); - *host_endian = 0; +#if !defined(HAVE_CLOCK_GETTIME) + mach_port_deallocate(mach_task_self(), clock_realtime); + mach_port_deallocate(mach_task_self(), clock_monotonic); +#endif + } - return 0; + pthread_mutex_unlock (&libusb_darwin_init_mutex); } -static int get_configuration_index (struct libusb_device *dev, int config_value) { +static int get_configuration_index (struct libusb_device *dev, UInt8 config_value) { struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); UInt8 i, numConfig; IOUSBConfigurationDescriptorPtr desc; @@ -615,7 +685,7 @@ static int get_configuration_index (struct libusb_device *dev, int config_value) return LIBUSB_ERROR_NOT_FOUND; } -static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsigned char *buffer, size_t len, int *host_endian) { +static int darwin_get_active_config_descriptor(struct libusb_device *dev, void *buffer, size_t len) { struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); int config_index; @@ -626,10 +696,11 @@ static int darwin_get_active_config_descriptor(struct libusb_device *dev, unsign if (config_index < 0) return config_index; - return darwin_get_config_descriptor (dev, config_index, buffer, len, host_endian); + assert(config_index >= 0 && config_index <= UINT8_MAX); + return darwin_get_config_descriptor (dev, (UInt8)config_index, buffer, len); } -static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, unsigned char *buffer, size_t len, int *host_endian) { +static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t config_index, void *buffer, size_t len) { struct darwin_cached_device *priv = DARWIN_CACHED_DEVICE(dev); IOUSBConfigurationDescriptorPtr desc; IOReturn kresult; @@ -645,9 +716,6 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi len = libusb_le16_to_cpu(desc->wTotalLength); memmove (buffer, desc, len); - - /* GetConfigurationDescriptorPtr returns the descriptor in USB bus order */ - *host_endian = 0; } ret = darwin_to_libusb (kresult); @@ -658,12 +726,12 @@ static int darwin_get_config_descriptor(struct libusb_device *dev, uint8_t confi } /* check whether the os has configured the device */ -static int darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { +static enum libusb_error darwin_check_configuration (struct libusb_context *ctx, struct darwin_cached_device *dev) { usb_device_t **darwin_device = dev->device; IOUSBConfigurationDescriptorPtr configDesc; IOUSBFindInterfaceRequest request; - kern_return_t kresult; + IOReturn kresult; io_iterator_t interface_iterator; io_service_t firstInterface; @@ -674,10 +742,11 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin /* checking the configuration of a root hub simulation takes ~1 s in 10.11. the device is not usable anyway */ - if (0x05ac == dev->dev_descriptor.idVendor && 0x8005 == dev->dev_descriptor.idProduct) { + if (0x05ac == libusb_le16_to_cpu (dev->dev_descriptor.idVendor) && + 0x8005 == libusb_le16_to_cpu (dev->dev_descriptor.idProduct)) { usbi_dbg ("ignoring configuration on root hub simulation"); dev->active_config = 0; - return 0; + return LIBUSB_SUCCESS; } /* find the first configuration */ @@ -695,7 +764,7 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin request.bAlternateSetting = kIOUSBFindInterfaceDontCare; kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); - if (kresult) + if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); /* iterate once */ @@ -717,23 +786,25 @@ static int darwin_check_configuration (struct libusb_context *ctx, struct darwin } else /* not configured */ dev->active_config = 0; - + usbi_dbg ("active config: %u, first config: %u", dev->active_config, dev->first_config); - return 0; + return LIBUSB_SUCCESS; } -static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { +static IOReturn darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 desc_index, void *buffer, size_t buffer_size) { IOUSBDevRequestTO req; + assert(buffer_size <= UINT16_MAX); + memset (buffer, 0, buffer_size); /* Set up request for descriptor/ */ req.bmRequestType = USBmakebmRequestType(kUSBIn, kUSBStandard, kUSBDevice); req.bRequest = kUSBRqGetDescriptor; - req.wValue = desc << 8; + req.wValue = (UInt16)(desc << 8); req.wIndex = desc_index; - req.wLength = buffer_size; + req.wLength = (UInt16)buffer_size; req.pData = buffer; req.noDataTimeout = 20; req.completionTimeout = 100; @@ -741,12 +812,13 @@ static int darwin_request_descriptor (usb_device_t **device, UInt8 desc, UInt8 d return (*device)->DeviceRequestTO (device, &req); } -static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct darwin_cached_device *dev) { +static enum libusb_error darwin_cache_device_descriptor (struct darwin_cached_device *dev) { usb_device_t **device = dev->device; - int retries = 1, delay = 30000; + int retries = 1; + long delay = 30000; // microseconds int unsuspended = 0, try_unsuspend = 1, try_reconfigure = 1; int is_open = 0; - int ret = 0, ret2; + IOReturn ret = 0, ret2; UInt8 bDeviceClass; UInt16 idProduct, idVendor; @@ -799,7 +871,7 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da (void)(*device)->GetUSBDeviceInformation (device, &info); /* note that the device was suspended */ - if (info & (1 << kUSBInformationDeviceIsSuspendedBit) || 0 == info) + if (info & (1U << kUSBInformationDeviceIsSuspendedBit) || 0 == info) try_unsuspend = 1; #endif @@ -818,9 +890,9 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da } if (kIOReturnSuccess != ret) { - usbi_dbg("kernel responded with code: 0x%08x. sleeping for %d ms before trying again", ret, delay/1000); + usbi_dbg("kernel responded with code: 0x%08x. sleeping for %ld ms before trying again", ret, delay/1000); /* sleep for a little while before trying again */ - nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000UL}, NULL); + nanosleep(&(struct timespec){delay / 1000000, (delay * 1000) % 1000000000}, NULL); } } while (kIOReturnSuccess != ret && retries--); @@ -837,7 +909,7 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da usbi_dbg ("could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", idVendor, idProduct, darwin_error_str (ret), ret); else - usbi_warn (ctx, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", + usbi_warn (NULL, "could not retrieve device descriptor %.4x:%.4x: %s (%x). skipping device", idVendor, idProduct, darwin_error_str (ret), ret); return darwin_to_libusb (ret); } @@ -845,21 +917,21 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da /* catch buggy hubs (which appear to be virtual). Apple's own USB prober has problems with these devices. */ if (libusb_le16_to_cpu (dev->dev_descriptor.idProduct) != idProduct) { /* not a valid device */ - usbi_warn (ctx, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device", + usbi_warn (NULL, "idProduct from iokit (%04x) does not match idProduct in descriptor (%04x). skipping device", idProduct, libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); return LIBUSB_ERROR_NO_DEVICE; } usbi_dbg ("cached device descriptor:"); usbi_dbg (" bDescriptorType: 0x%02x", dev->dev_descriptor.bDescriptorType); - usbi_dbg (" bcdUSB: 0x%04x", dev->dev_descriptor.bcdUSB); + usbi_dbg (" bcdUSB: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdUSB)); usbi_dbg (" bDeviceClass: 0x%02x", dev->dev_descriptor.bDeviceClass); usbi_dbg (" bDeviceSubClass: 0x%02x", dev->dev_descriptor.bDeviceSubClass); usbi_dbg (" bDeviceProtocol: 0x%02x", dev->dev_descriptor.bDeviceProtocol); usbi_dbg (" bMaxPacketSize0: 0x%02x", dev->dev_descriptor.bMaxPacketSize0); - usbi_dbg (" idVendor: 0x%04x", dev->dev_descriptor.idVendor); - usbi_dbg (" idProduct: 0x%04x", dev->dev_descriptor.idProduct); - usbi_dbg (" bcdDevice: 0x%04x", dev->dev_descriptor.bcdDevice); + usbi_dbg (" idVendor: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idVendor)); + usbi_dbg (" idProduct: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.idProduct)); + usbi_dbg (" bcdDevice: 0x%04x", libusb_le16_to_cpu (dev->dev_descriptor.bcdDevice)); usbi_dbg (" iManufacturer: 0x%02x", dev->dev_descriptor.iManufacturer); usbi_dbg (" iProduct: 0x%02x", dev->dev_descriptor.iProduct); usbi_dbg (" iSerialNumber: 0x%02x", dev->dev_descriptor.iSerialNumber); @@ -870,17 +942,18 @@ static int darwin_cache_device_descriptor (struct libusb_context *ctx, struct da return LIBUSB_SUCCESS; } -static int get_device_port (io_service_t service, UInt8 *port) { - kern_return_t result; +/* Returns 1 on success, 0 on failure. */ +static bool get_device_port (io_service_t service, UInt8 *port) { + IOReturn kresult; io_service_t parent; - int ret = 0; + bool ret = false; if (get_ioregistry_value_number (service, CFSTR("PortNum"), kCFNumberSInt8Type, port)) { - return 1; + return true; } - result = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent); - if (kIOReturnSuccess == result) { + kresult = IORegistryEntryGetParentEntry (service, kIOServicePlane, &parent); + if (kIOReturnSuccess == kresult) { ret = get_ioregistry_value_data (parent, CFSTR("port"), 1, port); IOObjectRelease (parent); } @@ -888,33 +961,40 @@ static int get_device_port (io_service_t service, UInt8 *port) { return ret; } -static int get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) { - kern_return_t result; +/* Returns 1 on success, 0 on failure. */ +static bool get_device_parent_sessionID(io_service_t service, UInt64 *parent_sessionID) { + IOReturn kresult; io_service_t parent; /* Walk up the tree in the IOService plane until we find a parent that has a sessionID */ parent = service; - while((result = IORegistryEntryGetParentEntry (parent, kIOServicePlane, &parent)) == kIOReturnSuccess) { + while((kresult = IORegistryEntryGetParentEntry (parent, kIOUSBPlane, &parent)) == kIOReturnSuccess) { if (get_ioregistry_value_number (parent, CFSTR("sessionID"), kCFNumberSInt64Type, parent_sessionID)) { /* Success */ - return 1; + return true; } } /* We ran out of parents */ - return 0; + return false; } -static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t service, - struct darwin_cached_device **cached_out) { +static enum libusb_error darwin_get_cached_device(io_service_t service, struct darwin_cached_device **cached_out, + UInt64 *old_session_id) { struct darwin_cached_device *new_device; UInt64 sessionID = 0, parent_sessionID = 0; - int ret = LIBUSB_SUCCESS; + UInt32 locationID = 0; + enum libusb_error ret = LIBUSB_SUCCESS; usb_device_t **device; UInt8 port = 0; + /* assuming sessionID != 0 normally (never seen it be 0) */ + *old_session_id = 0; + *cached_out = NULL; + /* get some info from the io registry */ (void) get_ioregistry_value_number (service, CFSTR("sessionID"), kCFNumberSInt64Type, &sessionID); + (void) get_ioregistry_value_number (service, CFSTR("locationID"), kCFNumberSInt32Type, &locationID); if (!get_device_port (service, &port)) { usbi_dbg("could not get connected port number"); } @@ -927,10 +1007,15 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser usbi_mutex_lock(&darwin_cached_devices_lock); do { - *cached_out = NULL; - list_for_each_entry(new_device, &darwin_cached_devices, list, struct darwin_cached_device) { - usbi_dbg("matching sessionID 0x%" PRIx64 " against cached device with sessionID 0x%" PRIx64, sessionID, new_device->session); + usbi_dbg("matching sessionID/locationID 0x%" PRIx64 "/0x%x against cached device with sessionID/locationID 0x%" PRIx64 "/0x%x", + sessionID, locationID, new_device->session, new_device->location); + if (new_device->location == locationID && new_device->in_reenumerate) { + usbi_dbg ("found cached device with matching location that is being re-enumerated"); + *old_session_id = new_device->session; + break; + } + if (new_device->session == sessionID) { usbi_dbg("using cached device for device"); *cached_out = new_device; @@ -949,89 +1034,104 @@ static int darwin_get_cached_device(struct libusb_context *ctx, io_service_t ser break; } - new_device = calloc (1, sizeof (*new_device)); - if (!new_device) { - ret = LIBUSB_ERROR_NO_MEM; - break; - } + if (!(*old_session_id)) { + new_device = calloc (1, sizeof (*new_device)); + if (!new_device) { + ret = LIBUSB_ERROR_NO_MEM; + break; + } - /* add this device to the cached device list */ - list_add(&new_device->list, &darwin_cached_devices); + /* add this device to the cached device list */ + list_add(&new_device->list, &darwin_cached_devices); - (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address); + (*device)->GetDeviceAddress (device, (USBDeviceAddress *)&new_device->address); - /* keep a reference to this device */ - darwin_ref_cached_device(new_device); + /* keep a reference to this device */ + darwin_ref_cached_device(new_device); + + (*device)->GetLocationID (device, &new_device->location); + new_device->port = port; + new_device->parent_session = parent_sessionID; + } + + /* keep track of devices regardless of if we successfully enumerate them to + prevent them from being enumerated multiple times */ + *cached_out = new_device; - new_device->device = device; new_device->session = sessionID; - (*device)->GetLocationID (device, &new_device->location); - new_device->port = port; - new_device->parent_session = parent_sessionID; + new_device->device = device; /* cache the device descriptor */ - ret = darwin_cache_device_descriptor(ctx, new_device); + ret = darwin_cache_device_descriptor(new_device); if (ret) break; if (new_device->can_enumerate) { snprintf(new_device->sys_path, 20, "%03i-%04x-%04x-%02x-%02x", new_device->address, - new_device->dev_descriptor.idVendor, new_device->dev_descriptor.idProduct, + libusb_le16_to_cpu (new_device->dev_descriptor.idVendor), + libusb_le16_to_cpu (new_device->dev_descriptor.idProduct), new_device->dev_descriptor.bDeviceClass, new_device->dev_descriptor.bDeviceSubClass); } } while (0); usbi_mutex_unlock(&darwin_cached_devices_lock); - /* keep track of devices regardless of if we successfully enumerate them to - prevent them from being enumerated multiple times */ - - *cached_out = new_device; - return ret; } -static int process_new_device (struct libusb_context *ctx, io_service_t service) { +static enum libusb_error process_new_device (struct libusb_context *ctx, struct darwin_cached_device *cached_device, + UInt64 old_session_id) { struct darwin_device_priv *priv; struct libusb_device *dev = NULL; - struct darwin_cached_device *cached_device; UInt8 devSpeed; - int ret = 0; + enum libusb_error ret = LIBUSB_SUCCESS; do { - ret = darwin_get_cached_device (ctx, service, &cached_device); - - if (ret < 0 || !cached_device->can_enumerate) { - return ret; - } - /* check current active configuration (and cache the first configuration value-- which may be used by claim_interface) */ ret = darwin_check_configuration (ctx, cached_device); if (ret) break; - usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64, - ctx, cached_device->session); + if (0 != old_session_id) { + usbi_dbg ("re-using existing device from context %p for with session 0x%" PRIx64 " new session 0x%" PRIx64, + ctx, old_session_id, cached_device->session); + /* save the libusb device before the session id is updated */ + dev = usbi_get_device_by_session_id (ctx, (unsigned long) old_session_id); + } - dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session); if (!dev) { - return LIBUSB_ERROR_NO_MEM; - } + usbi_dbg ("allocating new device in context %p for with session 0x%" PRIx64, + ctx, cached_device->session); - priv = (struct darwin_device_priv *)dev->os_priv; + dev = usbi_alloc_device(ctx, (unsigned long) cached_device->session); + if (!dev) { + return LIBUSB_ERROR_NO_MEM; + } - priv->dev = cached_device; - darwin_ref_cached_device (priv->dev); + priv = usbi_get_device_priv(dev); + + priv->dev = cached_device; + darwin_ref_cached_device (priv->dev); + dev->port_number = cached_device->port; + dev->bus_number = cached_device->location >> 24; + assert(cached_device->address <= UINT8_MAX); + dev->device_address = (uint8_t)cached_device->address; + } else { + priv = usbi_get_device_priv(dev); + } + + static_assert(sizeof(dev->device_descriptor) == sizeof(cached_device->dev_descriptor), + "mismatch between libusb and IOKit device descriptor sizes"); + memcpy(&dev->device_descriptor, &cached_device->dev_descriptor, LIBUSB_DT_DEVICE_SIZE); + usbi_localize_device_descriptor(&dev->device_descriptor); + dev->session_data = cached_device->session; if (cached_device->parent_session > 0) { dev->parent_dev = usbi_get_device_by_session_id (ctx, (unsigned long) cached_device->parent_session); } else { dev->parent_dev = NULL; } - dev->port_number = cached_device->port; - dev->bus_number = cached_device->location >> 24; - dev->device_address = cached_device->address; (*(priv->dev->device))->GetDeviceSpeed (priv->dev->device, &devSpeed); @@ -1055,9 +1155,10 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service) usbi_dbg ("found device with address %d port = %d parent = %p at %p", dev->device_address, dev->port_number, (void *) dev->parent_dev, priv->dev->sys_path); + } while (0); - if (0 == ret) { + if (!cached_device->in_reenumerate && 0 == ret) { usbi_connect_device (dev); } else { libusb_unref_device (dev); @@ -1066,28 +1167,36 @@ static int process_new_device (struct libusb_context *ctx, io_service_t service) return ret; } -static int darwin_scan_devices(struct libusb_context *ctx) { +static enum libusb_error darwin_scan_devices(struct libusb_context *ctx) { + struct darwin_cached_device *cached_device; + UInt64 old_session_id; io_iterator_t deviceIterator; io_service_t service; - kern_return_t kresult; + IOReturn kresult; + int ret; kresult = usb_setup_device_iterator (&deviceIterator, 0); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); while ((service = IOIteratorNext (deviceIterator))) { - (void) process_new_device (ctx, service); + ret = darwin_get_cached_device (service, &cached_device, &old_session_id); + if (ret < 0 || !cached_device->can_enumerate) { + continue; + } + + (void) process_new_device (ctx, cached_device, old_session_id); IOObjectRelease(service); } IOObjectRelease(deviceIterator); - return 0; + return LIBUSB_SUCCESS; } static int darwin_open (struct libusb_device_handle *dev_handle) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; @@ -1102,9 +1211,9 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { } /* it is possible to perform some actions on a device that is not open so do not return an error */ - priv->is_open = 0; + priv->is_open = false; } else { - priv->is_open = 1; + priv->is_open = true; } /* create async event source */ @@ -1116,7 +1225,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { (*(dpriv->device))->USBDeviceClose (dpriv->device); } - priv->is_open = 0; + priv->is_open = false; return darwin_to_libusb (kresult); } @@ -1136,7 +1245,7 @@ static int darwin_open (struct libusb_device_handle *dev_handle) { } static void darwin_close (struct libusb_device_handle *dev_handle) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; int i; @@ -1151,7 +1260,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { /* make sure all interfaces are released */ for (i = 0 ; i < USB_MAXINTERFACES ; i++) - if (dev_handle->claimed_interfaces & (1 << i)) + if (dev_handle->claimed_interfaces & (1U << i)) libusb_release_interface (dev_handle, i); if (0 == dpriv->open_count) { @@ -1166,7 +1275,7 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { if (priv->is_open) { /* close the device */ kresult = (*(dpriv->device))->USBDeviceClose(dpriv->device); - if (kresult) { + if (kresult != kIOReturnSuccess) { /* Log the fact that we had a problem closing the file, however failing a * close isn't really an error, so return success anyway */ usbi_warn (HANDLE_CTX (dev_handle), "USBDeviceClose: %s", darwin_error_str(kresult)); @@ -1175,45 +1284,48 @@ static void darwin_close (struct libusb_device_handle *dev_handle) { } } -static int darwin_get_configuration(struct libusb_device_handle *dev_handle, int *config) { +static int darwin_get_configuration(struct libusb_device_handle *dev_handle, uint8_t *config) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); - *config = (int) dpriv->active_config; + *config = dpriv->active_config; - return 0; + return LIBUSB_SUCCESS; } -static int darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { +static enum libusb_error darwin_set_configuration(struct libusb_device_handle *dev_handle, int config) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); IOReturn kresult; - int i; + uint8_t i; + + if (config == -1) + config = 0; /* Setting configuration will invalidate the interface, so we need to reclaim it. First, dispose of existing interfaces, if any. */ for (i = 0 ; i < USB_MAXINTERFACES ; i++) - if (dev_handle->claimed_interfaces & (1 << i)) + if (dev_handle->claimed_interfaces & (1U << i)) darwin_release_interface (dev_handle, i); - kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, config); + kresult = (*(dpriv->device))->SetConfiguration (dpriv->device, (UInt8)config); if (kresult != kIOReturnSuccess) return darwin_to_libusb (kresult); /* Reclaim any interfaces. */ for (i = 0 ; i < USB_MAXINTERFACES ; i++) - if (dev_handle->claimed_interfaces & (1 << i)) + if (dev_handle->claimed_interfaces & (1U << i)) darwin_claim_interface (dev_handle, i); - dpriv->active_config = config; + dpriv->active_config = (UInt8)config; - return 0; + return LIBUSB_SUCCESS; } -static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { +static IOReturn darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_service_t *usbInterfacep) { IOUSBFindInterfaceRequest request; - kern_return_t kresult; + IOReturn kresult; io_iterator_t interface_iterator; UInt8 bInterfaceNumber; - int ret; + bool ret; *usbInterfacep = IO_OBJECT_NULL; @@ -1224,7 +1336,7 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s request.bAlternateSetting = kIOUSBFindInterfaceDontCare; kresult = (*(darwin_device))->CreateInterfaceIterator(darwin_device, &request, &interface_iterator); - if (kresult) + if (kresult != kIOReturnSuccess) return kresult; while ((*usbInterfacep = IOIteratorNext(interface_iterator))) { @@ -1242,16 +1354,16 @@ static int darwin_get_interface (usb_device_t **darwin_device, uint8_t ifc, io_s /* done with the interface iterator */ IOObjectRelease(interface_iterator); - return 0; + return kIOReturnSuccess; } -static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; +static enum libusb_error get_endpoints (struct libusb_device_handle *dev_handle, uint8_t iface) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; - kern_return_t kresult; + IOReturn kresult; UInt8 numep, direction, number; UInt8 dont_care1, dont_care3; @@ -1262,13 +1374,13 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { /* retrieve the total number of endpoints on this interface */ kresult = (*(cInterface->interface))->GetNumEndpoints(cInterface->interface, &numep); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "can't get number of endpoints for interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } /* iterate through pipe references */ - for (int i = 1 ; i <= numep ; i++) { + for (UInt8 i = 1 ; i <= numep ; i++) { kresult = (*(cInterface->interface))->GetPipeProperties(cInterface->interface, i, &direction, &number, &dont_care1, &dont_care2, &dont_care3); @@ -1279,7 +1391,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { UInt8 alt_setting; kresult = (*(cInterface->interface))->GetAlternateSetting (cInterface->interface, &alt_setting); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "can't get alternate setting for interface"); return darwin_to_libusb (kresult); } @@ -1293,7 +1405,7 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { cInterface->endpoint_addrs[i - 1] = endpoint_desc->bEndpointAddress; } else { - cInterface->endpoint_addrs[i - 1] = (((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); + cInterface->endpoint_addrs[i - 1] = (UInt8)(((kUSBIn == direction) << kUSBRqDirnShift) | (number & LIBUSB_ENDPOINT_ADDRESS_MASK)); } usbi_dbg ("interface: %i pipe %i: dir: %i number: %i", iface, i, cInterface->endpoint_addrs[i - 1] >> kUSBRqDirnShift, @@ -1302,14 +1414,15 @@ static int get_endpoints (struct libusb_device_handle *dev_handle, int iface) { cInterface->num_endpoints = numep; - return 0; + return LIBUSB_SUCCESS; } -static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int iface) { +static int darwin_claim_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); io_service_t usbInterface = IO_OBJECT_NULL; - IOReturn kresult; + IOReturn kresult; + enum libusb_error ret; IOCFPlugInInterface **plugInInterface = NULL; SInt32 score; @@ -1325,14 +1438,14 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i usbi_info (HANDLE_CTX (dev_handle), "no interface found; setting configuration: %d", dpriv->first_config); /* set the configuration */ - kresult = darwin_set_configuration (dev_handle, dpriv->first_config); - if (kresult != LIBUSB_SUCCESS) { + ret = darwin_set_configuration (dev_handle, (int) dpriv->first_config); + if (ret != LIBUSB_SUCCESS) { usbi_err (HANDLE_CTX (dev_handle), "could not set configuration"); - return kresult; + return ret; } kresult = darwin_get_interface (dpriv->device, iface, &usbInterface); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } @@ -1350,7 +1463,7 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i /* ignore release error */ (void)IOObjectRelease (usbInterface); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "IOCreatePlugInInterfaceForService: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } @@ -1362,30 +1475,30 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i /* Do the actual claim */ kresult = (*plugInInterface)->QueryInterface(plugInInterface, - CFUUIDGetUUIDBytes(kIOUSBInterfaceInterfaceID), + CFUUIDGetUUIDBytes(InterfaceInterfaceID), (LPVOID)&cInterface->interface); /* We no longer need the intermediate plug-in */ /* Use release instead of IODestroyPlugInInterface to avoid stopping IOServices associated with this device */ (*plugInInterface)->Release (plugInInterface); - if (kresult || !cInterface->interface) { + if (kresult != kIOReturnSuccess || !cInterface->interface) { usbi_err (HANDLE_CTX (dev_handle), "QueryInterface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } /* claim the interface */ kresult = (*(cInterface->interface))->USBInterfaceOpen(cInterface->interface); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "USBInterfaceOpen: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); } /* update list of endpoints */ - kresult = get_endpoints (dev_handle, iface); - if (kresult) { + ret = get_endpoints (dev_handle, iface); + if (ret) { /* this should not happen */ darwin_release_interface (dev_handle, iface); usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); - return kresult; + return ret; } cInterface->cfSource = NULL; @@ -1406,11 +1519,11 @@ static int darwin_claim_interface(struct libusb_device_handle *dev_handle, int i usbi_dbg ("interface opened"); - return 0; + return LIBUSB_SUCCESS; } -static int darwin_release_interface(struct libusb_device_handle *dev_handle, int iface) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; +static int darwin_release_interface(struct libusb_device_handle *dev_handle, uint8_t iface) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); IOReturn kresult; /* current interface */ @@ -1430,7 +1543,7 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int } kresult = (*(cInterface->interface))->USBInterfaceClose(cInterface->interface); - if (kresult) + if (kresult != kIOReturnSuccess) usbi_warn (HANDLE_CTX (dev_handle), "USBInterfaceClose: %s", darwin_error_str(kresult)); kresult = (*(cInterface->interface))->Release(cInterface->interface); @@ -1442,9 +1555,10 @@ static int darwin_release_interface(struct libusb_device_handle *dev_handle, int return darwin_to_libusb (kresult); } -static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, int iface, int altsetting) { - struct darwin_device_handle_priv *priv = (struct darwin_device_handle_priv *)dev_handle->os_priv; +static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_handle, uint8_t iface, uint8_t altsetting) { + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); IOReturn kresult; + enum libusb_error ret; /* current interface */ struct darwin_interface *cInterface = &priv->interfaces[iface]; @@ -1457,12 +1571,12 @@ static int darwin_set_interface_altsetting(struct libusb_device_handle *dev_hand darwin_reset_device (dev_handle); /* update list of endpoints */ - kresult = get_endpoints (dev_handle, iface); - if (kresult) { + ret = get_endpoints (dev_handle, iface); + if (ret) { /* this should not happen */ darwin_release_interface (dev_handle, iface); usbi_err (HANDLE_CTX (dev_handle), "could not build endpoint table"); - return kresult; + return ret; } return darwin_to_libusb (kresult); @@ -1483,72 +1597,143 @@ static int darwin_clear_halt(struct libusb_device_handle *dev_handle, unsigned c /* newer versions of darwin support clearing additional bits on the device's endpoint */ kresult = (*(cInterface->interface))->ClearPipeStallBothEnds(cInterface->interface, pipeRef); - if (kresult) + if (kresult != kIOReturnSuccess) usbi_warn (HANDLE_CTX (dev_handle), "ClearPipeStall: %s", darwin_error_str (kresult)); return darwin_to_libusb (kresult); } +static int darwin_restore_state (struct libusb_device_handle *dev_handle, int8_t active_config, + unsigned long claimed_interfaces) { + struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + struct darwin_device_handle_priv *priv = usbi_get_device_handle_priv(dev_handle); + int open_count = dpriv->open_count; + int ret; + + /* clear claimed interfaces temporarily */ + dev_handle->claimed_interfaces = 0; + + /* close and re-open the device */ + priv->is_open = false; + dpriv->open_count = 1; + + /* clean up open interfaces */ + (void) darwin_close (dev_handle); + + /* re-open the device */ + ret = darwin_open (dev_handle); + dpriv->open_count = open_count; + if (LIBUSB_SUCCESS != ret) { + /* could not restore configuration */ + return LIBUSB_ERROR_NOT_FOUND; + } + + if (dpriv->active_config != active_config) { + usbi_dbg ("darwin/restore_state: restoring configuration %d...", active_config); + + ret = darwin_set_configuration (dev_handle, active_config); + if (LIBUSB_SUCCESS != ret) { + usbi_dbg ("darwin/restore_state: could not restore configuration"); + return LIBUSB_ERROR_NOT_FOUND; + } + } + + usbi_dbg ("darwin/restore_state: reclaiming interfaces"); + + if (claimed_interfaces) { + for (uint8_t iface = 0 ; iface < USB_MAXINTERFACES ; ++iface) { + if (!(claimed_interfaces & (1U << iface))) { + continue; + } + + usbi_dbg ("darwin/restore_state: re-claiming interface %u", iface); + + ret = darwin_claim_interface (dev_handle, iface); + if (LIBUSB_SUCCESS != ret) { + usbi_dbg ("darwin/restore_state: could not claim interface %u", iface); + return LIBUSB_ERROR_NOT_FOUND; + } + + dev_handle->claimed_interfaces |= 1U << iface; + } + } + + usbi_dbg ("darwin/restore_state: device state restored"); + + return LIBUSB_SUCCESS; +} + static int darwin_reset_device(struct libusb_device_handle *dev_handle) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); + unsigned long claimed_interfaces = dev_handle->claimed_interfaces; + int8_t active_config = dpriv->active_config; IOUSBDeviceDescriptor descriptor; IOUSBConfigurationDescriptorPtr cached_configuration; - IOUSBConfigurationDescriptor configuration; - bool reenumerate = false; + IOUSBConfigurationDescriptor *cached_configurations; IOReturn kresult; - int i; + UInt8 i; - kresult = (*(dpriv->device))->ResetDevice (dpriv->device); - if (kresult) { - usbi_err (HANDLE_CTX (dev_handle), "ResetDevice: %s", darwin_error_str (kresult)); - return darwin_to_libusb (kresult); + if (dpriv->in_reenumerate) { + /* ack, two (or more) threads are trying to reset the device! abort! */ + return LIBUSB_ERROR_NOT_FOUND; } - do { - usbi_dbg ("darwin/reset_device: checking if device descriptor changed"); + dpriv->in_reenumerate = true; - /* ignore return code. if we can't get a descriptor it might be worthwhile re-enumerating anway */ - (void) darwin_request_descriptor (dpriv->device, kUSBDeviceDesc, 0, &descriptor, sizeof (descriptor)); + /* store copies of descriptors so they can be compared after the reset */ + memcpy (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor)); + cached_configurations = alloca (sizeof (*cached_configurations) * descriptor.bNumConfigurations); - /* check if the device descriptor has changed */ - if (0 != memcmp (&dpriv->dev_descriptor, &descriptor, sizeof (descriptor))) { - reenumerate = true; - break; - } + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + memcpy (cached_configurations + i, cached_configuration, sizeof (cached_configurations[i])); + } - /* check if any configuration descriptor has changed */ - for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { - usbi_dbg ("darwin/reset_device: checking if configuration descriptor %d changed", i); + /* from macOS 10.11 ResetDevice no longer does anything so just use USBDeviceReEnumerate */ + kresult = (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0); + if (kresult != kIOReturnSuccess) { + usbi_err (HANDLE_CTX (dev_handle), "USBDeviceReEnumerate: %s", darwin_error_str (kresult)); + dpriv->in_reenumerate = false; + return darwin_to_libusb (kresult); + } - (void) darwin_request_descriptor (dpriv->device, kUSBConfDesc, i, &configuration, sizeof (configuration)); - (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + usbi_dbg ("darwin/reset_device: waiting for re-enumeration to complete..."); - if (!cached_configuration || 0 != memcmp (cached_configuration, &configuration, sizeof (configuration))) { - reenumerate = true; - break; - } - } - } while (0); + while (dpriv->in_reenumerate) { + struct timespec delay = {.tv_sec = 0, .tv_nsec = 1000}; + nanosleep (&delay, NULL); + } + + /* compare descriptors */ + usbi_dbg ("darwin/reset_device: checking whether descriptors changed"); - if (reenumerate) { - usbi_dbg ("darwin/reset_device: device requires reenumeration"); - (void) (*(dpriv->device))->USBDeviceReEnumerate (dpriv->device, 0); + if (memcmp (&descriptor, &dpriv->dev_descriptor, sizeof (descriptor))) { + /* device descriptor changed. need to return not found. */ + usbi_dbg ("darwin/reset_device: device descriptor changed"); return LIBUSB_ERROR_NOT_FOUND; } - usbi_dbg ("darwin/reset_device: device reset complete"); + for (i = 0 ; i < descriptor.bNumConfigurations ; ++i) { + (void) (*(dpriv->device))->GetConfigurationDescriptorPtr (dpriv->device, i, &cached_configuration); + if (memcmp (cached_configuration, cached_configurations + i, sizeof (cached_configurations[i]))) { + usbi_dbg ("darwin/reset_device: configuration descriptor %d changed", i); + return LIBUSB_ERROR_NOT_FOUND; + } + } - return LIBUSB_SUCCESS; + usbi_dbg ("darwin/reset_device: device reset complete. restoring state..."); + + return darwin_restore_state (dev_handle, active_config, claimed_interfaces); } -static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, int interface) { +static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, uint8_t interface) { struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(dev_handle->dev); io_service_t usbInterface; CFTypeRef driver; IOReturn kresult; kresult = darwin_get_interface (dpriv->device, interface, &usbInterface); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (HANDLE_CTX (dev_handle), "darwin_get_interface: %s", darwin_error_str(kresult)); return darwin_to_libusb (kresult); @@ -1567,21 +1752,8 @@ static int darwin_kernel_driver_active(struct libusb_device_handle *dev_handle, return 0; } -/* attaching/detaching kernel drivers is not currently supported (maybe in the future?) */ -static int darwin_attach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { - UNUSED(dev_handle); - UNUSED(interface); - return LIBUSB_ERROR_NOT_SUPPORTED; -} - -static int darwin_detach_kernel_driver (struct libusb_device_handle *dev_handle, int interface) { - UNUSED(dev_handle); - UNUSED(interface); - return LIBUSB_ERROR_NOT_SUPPORTED; -} - static void darwin_destroy_device(struct libusb_device *dev) { - struct darwin_device_priv *dpriv = (struct darwin_device_priv *) dev->os_priv; + struct darwin_device_priv *dpriv = usbi_get_device_priv(dev); if (dpriv->dev) { /* need to hold the lock in case this is the last reference to the device */ @@ -1597,11 +1769,16 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { IOReturn ret; uint8_t transferType; - /* None of the values below are used in libusbx for bulk transfers */ - uint8_t direction, number, interval, pipeRef; + uint8_t pipeRef; uint16_t maxPacketSize; struct darwin_interface *cInterface; +#if InterfaceVersion >= 550 + IOUSBEndpointProperties pipeProperties = {.bVersion = kUSBEndpointPropertiesVersion3}; +#else + /* None of the values below are used in libusb for bulk transfers */ + uint8_t direction, number, interval; +#endif if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { usbi_err (TRANSFER_CTX (transfer), "endpoint not found on any open interface"); @@ -1609,8 +1786,15 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { return LIBUSB_ERROR_NOT_FOUND; } +#if InterfaceVersion >= 550 + ret = (*(cInterface->interface))->GetPipePropertiesV3 (cInterface->interface, pipeRef, &pipeProperties); + + transferType = pipeProperties.bTransferType; + maxPacketSize = pipeProperties.wMaxPacketSize; +#else ret = (*(cInterface->interface))->GetPipeProperties (cInterface->interface, pipeRef, &direction, &number, &transferType, &maxPacketSize, &interval); +#endif if (ret) { usbi_err (TRANSFER_CTX (transfer), "bulk transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", @@ -1628,21 +1812,21 @@ static int submit_bulk_transfer(struct usbi_transfer *itransfer) { if (transferType == kUSBInterrupt) { if (IS_XFERIN(transfer)) ret = (*(cInterface->interface))->ReadPipeAsync(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, darwin_async_io_callback, itransfer); + (UInt32)transfer->length, darwin_async_io_callback, itransfer); else ret = (*(cInterface->interface))->WritePipeAsync(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, darwin_async_io_callback, itransfer); + (UInt32)transfer->length, darwin_async_io_callback, itransfer); } else { itransfer->timeout_flags |= USBI_TRANSFER_OS_HANDLES_TIMEOUT; if (IS_XFERIN(transfer)) ret = (*(cInterface->interface))->ReadPipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, (void *)itransfer); + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); else ret = (*(cInterface->interface))->WritePipeAsyncTO(cInterface->interface, pipeRef, transfer->buffer, - transfer->length, transfer->timeout, transfer->timeout, - darwin_async_io_callback, (void *)itransfer); + (UInt32)transfer->length, transfer->timeout, transfer->timeout, + darwin_async_io_callback, itransfer); } if (ret) @@ -1669,12 +1853,12 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) { if (IS_XFERIN(transfer)) ret = (*(cInterface->interface))->ReadStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, - transfer->buffer, transfer->length, transfer->timeout, - transfer->timeout, darwin_async_io_callback, (void *)itransfer); + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); else ret = (*(cInterface->interface))->WriteStreamsPipeAsyncTO(cInterface->interface, pipeRef, itransfer->stream_id, - transfer->buffer, transfer->length, transfer->timeout, - transfer->timeout, darwin_async_io_callback, (void *)itransfer); + transfer->buffer, (UInt32)transfer->length, transfer->timeout, + transfer->timeout, darwin_async_io_callback, itransfer); if (ret) usbi_err (TRANSFER_CTX (transfer), "bulk stream transfer failed (dir = %s): %s (code = 0x%08x)", IS_XFERIN(transfer) ? "In" : "Out", @@ -1686,7 +1870,7 @@ static int submit_stream_transfer(struct usbi_transfer *itransfer) { static int submit_iso_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); IOReturn kresult; uint8_t direction, number, interval, pipeRef, transferType; @@ -1697,22 +1881,25 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { struct darwin_interface *cInterface; - /* construct an array of IOUSBIsocFrames, reuse the old one if possible */ - if (tpriv->isoc_framelist && tpriv->num_iso_packets != transfer->num_iso_packets) { + /* construct an array of IOUSBIsocFrames, reuse the old one if the sizes are the same */ + if (tpriv->num_iso_packets != transfer->num_iso_packets) { free(tpriv->isoc_framelist); tpriv->isoc_framelist = NULL; } if (!tpriv->isoc_framelist) { tpriv->num_iso_packets = transfer->num_iso_packets; - tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc (transfer->num_iso_packets, sizeof(IOUSBIsocFrame)); + tpriv->isoc_framelist = (IOUSBIsocFrame*) calloc ((size_t)transfer->num_iso_packets, sizeof(IOUSBIsocFrame)); if (!tpriv->isoc_framelist) return LIBUSB_ERROR_NO_MEM; } /* copy the frame list from the libusb descriptor (the structures differ only is member order) */ - for (i = 0 ; i < transfer->num_iso_packets ; i++) - tpriv->isoc_framelist[i].frReqCount = transfer->iso_packet_desc[i].length; + for (i = 0 ; i < transfer->num_iso_packets ; i++) { + unsigned int length = transfer->iso_packet_desc[i].length; + assert(length <= UINT16_MAX); + tpriv->isoc_framelist[i].frReqCount = (UInt16)length; + } /* determine the interface/endpoint to use */ if (ep_to_pipeRef (transfer->dev_handle, transfer->endpoint, &pipeRef, NULL, &cInterface) != 0) { @@ -1727,7 +1914,7 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { /* Last but not least we need the bus frame number */ kresult = (*(cInterface->interface))->GetBusFrameNumber(cInterface->interface, &frame, &atTime); - if (kresult) { + if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "failed to get bus frame number: %d", kresult); free(tpriv->isoc_framelist); tpriv->isoc_framelist = NULL; @@ -1747,19 +1934,19 @@ static int submit_iso_transfer(struct usbi_transfer *itransfer) { /* submit the request */ if (IS_XFERIN(transfer)) kresult = (*(cInterface->interface))->ReadIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, itransfer); else kresult = (*(cInterface->interface))->WriteIsochPipeAsync(cInterface->interface, pipeRef, transfer->buffer, frame, - transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, + (UInt32)transfer->num_iso_packets, tpriv->isoc_framelist, darwin_async_io_callback, itransfer); if (LIBUSB_SPEED_FULL == transfer->dev_handle->dev->speed) /* Full speed */ - cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)); + cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)); else /* High/super speed */ - cInterface->frames[transfer->endpoint] = frame + transfer->num_iso_packets * (1 << (interval - 1)) / 8; + cInterface->frames[transfer->endpoint] = frame + (UInt32)transfer->num_iso_packets * (1U << (interval - 1)) / 8; if (kresult != kIOReturnSuccess) { usbi_err (TRANSFER_CTX (transfer), "isochronous transfer failed (dir: %s): %s", IS_XFERIN(transfer) ? "In" : "Out", @@ -1775,7 +1962,7 @@ static int submit_control_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); struct libusb_control_setup *setup = (struct libusb_control_setup *) transfer->buffer; struct darwin_cached_device *dpriv = DARWIN_CACHED_DEVICE(transfer->dev_handle->dev); - struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); IOReturn kresult; @@ -1907,20 +2094,10 @@ static int darwin_cancel_transfer(struct usbi_transfer *itransfer) { } } -static void darwin_clear_transfer_priv (struct usbi_transfer *itransfer) { - struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); - - if (transfer->type == LIBUSB_TRANSFER_TYPE_ISOCHRONOUS && tpriv->isoc_framelist) { - free (tpriv->isoc_framelist); - tpriv->isoc_framelist = NULL; - } -} - static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) { struct usbi_transfer *itransfer = (struct usbi_transfer *)refcon; struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); usbi_dbg ("an async io operation has completed"); @@ -1941,7 +2118,7 @@ static void darwin_async_io_callback (void *refcon, IOReturn result, void *arg0) usbi_signal_transfer_completion(itransfer); } -static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_t result) { +static enum libusb_transfer_status darwin_transfer_status (struct usbi_transfer *itransfer, IOReturn result) { if (itransfer->timeout_flags & USBI_TRANSFER_TIMED_OUT) result = kIOUSBTransactionTimeout; @@ -1969,73 +2146,62 @@ static int darwin_transfer_status (struct usbi_transfer *itransfer, kern_return_ static int darwin_handle_transfer_completion (struct usbi_transfer *itransfer) { struct libusb_transfer *transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); - struct darwin_transfer_priv *tpriv = usbi_transfer_get_os_priv(itransfer); - int isIsoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; - int isBulk = LIBUSB_TRANSFER_TYPE_BULK == transfer->type; - int isControl = LIBUSB_TRANSFER_TYPE_CONTROL == transfer->type; - int isInterrupt = LIBUSB_TRANSFER_TYPE_INTERRUPT == transfer->type; - int i; + struct darwin_transfer_priv *tpriv = usbi_get_transfer_priv(itransfer); + const unsigned char max_transfer_type = LIBUSB_TRANSFER_TYPE_BULK_STREAM; + const char *transfer_types[max_transfer_type + 1] = {"control", "isoc", "bulk", "interrupt", "bulk-stream"}; + bool is_isoc = LIBUSB_TRANSFER_TYPE_ISOCHRONOUS == transfer->type; - if (!isIsoc && !isBulk && !isControl && !isInterrupt) { + if (transfer->type > max_transfer_type) { usbi_err (TRANSFER_CTX(transfer), "unknown endpoint type %d", transfer->type); return LIBUSB_ERROR_INVALID_PARAM; } - usbi_dbg ("handling %s completion with kernel status %d", - isControl ? "control" : isBulk ? "bulk" : isIsoc ? "isoc" : "interrupt", tpriv->result); + if (NULL == tpriv) { + usbi_err (TRANSFER_CTX(transfer), "malformed request is missing transfer priv"); + return LIBUSB_ERROR_INVALID_PARAM; + } + + usbi_dbg ("handling transfer completion type %s with kernel status %d", transfer_types[transfer->type], tpriv->result); if (kIOReturnSuccess == tpriv->result || kIOReturnUnderrun == tpriv->result) { - if (isIsoc && tpriv->isoc_framelist) { + if (is_isoc && tpriv->isoc_framelist) { /* copy isochronous results back */ - for (i = 0; i < transfer->num_iso_packets ; i++) { + for (int i = 0; i < transfer->num_iso_packets ; i++) { struct libusb_iso_packet_descriptor *lib_desc = &transfer->iso_packet_desc[i]; - lib_desc->status = darwin_to_libusb (tpriv->isoc_framelist[i].frStatus); + lib_desc->status = darwin_transfer_status (itransfer, tpriv->isoc_framelist[i].frStatus); lib_desc->actual_length = tpriv->isoc_framelist[i].frActCount; } - } else if (!isIsoc) + } else if (!is_isoc) { itransfer->transferred += tpriv->size; + } } /* it is ok to handle cancelled transfers without calling usbi_handle_transfer_cancellation (we catch timeout transfers) */ return usbi_handle_transfer_completion (itransfer, darwin_transfer_status (itransfer, tpriv->result)); } -static int darwin_clock_gettime(int clk_id, struct timespec *tp) { -#if !OSX_USE_CLOCK_GETTIME +#if !defined(HAVE_CLOCK_GETTIME) +void usbi_get_monotonic_time(struct timespec *tp) { mach_timespec_t sys_time; - clock_serv_t clock_ref; - - switch (clk_id) { - case USBI_CLOCK_REALTIME: - /* CLOCK_REALTIME represents time since the epoch */ - clock_ref = clock_realtime; - break; - case USBI_CLOCK_MONOTONIC: - /* use system boot time as reference for the monotonic clock */ - clock_ref = clock_monotonic; - break; - default: - return LIBUSB_ERROR_INVALID_PARAM; - } - clock_get_time (clock_ref, &sys_time); + /* use system boot time as reference for the monotonic clock */ + clock_get_time (clock_monotonic, &sys_time); tp->tv_sec = sys_time.tv_sec; tp->tv_nsec = sys_time.tv_nsec; +} - return 0; -#else - switch (clk_id) { - case USBI_CLOCK_MONOTONIC: - return clock_gettime(CLOCK_MONOTONIC, tp); - case USBI_CLOCK_REALTIME: - return clock_gettime(CLOCK_REALTIME, tp); - default: - return LIBUSB_ERROR_INVALID_PARAM; - } -#endif +void usbi_get_real_time(struct timespec *tp) { + mach_timespec_t sys_time; + + /* CLOCK_REALTIME represents time since the epoch */ + clock_get_time (clock_realtime, &sys_time); + + tp->tv_sec = sys_time.tv_sec; + tp->tv_nsec = sys_time.tv_nsec; } +#endif #if InterfaceVersion >= 550 static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32_t num_streams, unsigned char *endpoints, @@ -2045,7 +2211,7 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32 uint8_t pipeRef; int rc, i; - /* find the mimimum number of supported streams on the endpoint list */ + /* find the minimum number of supported streams on the endpoint list */ for (i = 0 ; i < num_endpoints ; ++i) { if (0 != (rc = ep_to_pipeRef (dev_handle, endpoints[i], &pipeRef, NULL, &cInterface))) { return rc; @@ -2069,7 +2235,8 @@ static int darwin_alloc_streams (struct libusb_device_handle *dev_handle, uint32 return darwin_to_libusb(rc); } - return num_streams; + assert(num_streams <= INT_MAX); + return (int)num_streams; } static int darwin_free_streams (struct libusb_device_handle *dev_handle, unsigned char *endpoints, int num_endpoints) { @@ -2100,8 +2267,6 @@ const struct usbi_os_backend usbi_backend = { .caps = 0, .init = darwin_init, .exit = darwin_exit, - .get_device_list = NULL, /* not needed */ - .get_device_descriptor = darwin_get_device_descriptor, .get_active_config_descriptor = darwin_get_active_config_descriptor, .get_config_descriptor = darwin_get_config_descriptor, .hotplug_poll = darwin_hotplug_poll, @@ -2123,19 +2288,14 @@ const struct usbi_os_backend usbi_backend = { #endif .kernel_driver_active = darwin_kernel_driver_active, - .detach_kernel_driver = darwin_detach_kernel_driver, - .attach_kernel_driver = darwin_attach_kernel_driver, .destroy_device = darwin_destroy_device, .submit_transfer = darwin_submit_transfer, .cancel_transfer = darwin_cancel_transfer, - .clear_transfer_priv = darwin_clear_transfer_priv, .handle_transfer_completion = darwin_handle_transfer_completion, - .clock_gettime = darwin_clock_gettime, - .device_priv_size = sizeof(struct darwin_device_priv), .device_handle_priv_size = sizeof(struct darwin_device_handle_priv), .transfer_priv_size = sizeof(struct darwin_transfer_priv), diff --git a/mac/libusb/os/darwin_usb.h b/mac/libusb/os/darwin_usb.h index 474567f6a..b799bfd44 100644 --- a/mac/libusb/os/darwin_usb.h +++ b/mac/libusb/os/darwin_usb.h @@ -1,6 +1,7 @@ /* * darwin backend for libusb 1.0 - * Copyright © 2008-2015 Nathan Hjelm + * Copyright © 2008-2019 Nathan Hjelm + * Copyright © 2019 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -20,6 +21,8 @@ #if !defined(LIBUSB_DARWIN_H) #define LIBUSB_DARWIN_H +#include + #include "libusbi.h" #include @@ -155,13 +158,14 @@ struct darwin_cached_device { UInt32 location; UInt64 parent_session; UInt64 session; - UInt16 address; + USBDeviceAddress address; char sys_path[21]; usb_device_t **device; int open_count; - UInt8 first_config, active_config, port; + UInt8 first_config, active_config, port; int can_enumerate; int refcount; + bool in_reenumerate; }; struct darwin_device_priv { @@ -169,7 +173,7 @@ struct darwin_device_priv { }; struct darwin_device_handle_priv { - int is_open; + bool is_open; CFRunLoopSourceRef cfSource; struct darwin_interface { diff --git a/mac/libusb/os/events_posix.c b/mac/libusb/os/events_posix.c new file mode 100644 index 000000000..b74189b94 --- /dev/null +++ b/mac/libusb/os/events_posix.c @@ -0,0 +1,300 @@ +/* + * libusb event abstraction on POSIX platforms + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "libusbi.h" + +#include +#include +#ifdef HAVE_EVENTFD +#include +#endif +#ifdef HAVE_TIMERFD +#include +#endif +#include + +#ifdef HAVE_EVENTFD +#define EVENT_READ_FD(e) ((e)->eventfd) +#define EVENT_WRITE_FD(e) ((e)->eventfd) +#else +#define EVENT_READ_FD(e) ((e)->pipefd[0]) +#define EVENT_WRITE_FD(e) ((e)->pipefd[1]) +#endif + +#ifdef HAVE_NFDS_T +typedef nfds_t usbi_nfds_t; +#else +typedef unsigned int usbi_nfds_t; +#endif + +int usbi_create_event(usbi_event_t *event) +{ +#ifdef HAVE_EVENTFD + event->eventfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); + if (event->eventfd == -1) { + usbi_err(NULL, "failed to create eventfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +#else +#if defined(HAVE_PIPE2) + int ret = pipe2(event->pipefd, O_CLOEXEC); +#else + int ret = pipe(event->pipefd); +#endif + + if (ret != 0) { + usbi_err(NULL, "failed to create pipe, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + +#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC) + ret = fcntl(event->pipefd[0], F_GETFD); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[0], F_SETFD, ret | FD_CLOEXEC); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + + ret = fcntl(event->pipefd[1], F_GETFD); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[1], F_SETFD, ret | FD_CLOEXEC); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd flags, errno=%d", errno); + goto err_close_pipe; + } +#endif + + ret = fcntl(event->pipefd[1], F_GETFL); + if (ret == -1) { + usbi_err(NULL, "failed to get pipe fd status flags, errno=%d", errno); + goto err_close_pipe; + } + ret = fcntl(event->pipefd[1], F_SETFL, ret | O_NONBLOCK); + if (ret == -1) { + usbi_err(NULL, "failed to set pipe fd status flags, errno=%d", errno); + goto err_close_pipe; + } + + return 0; + +err_close_pipe: + close(event->pipefd[1]); + close(event->pipefd[0]); + return LIBUSB_ERROR_OTHER; +#endif +} + +void usbi_destroy_event(usbi_event_t *event) +{ +#ifdef HAVE_EVENTFD + if (close(event->eventfd) == -1) + usbi_warn(NULL, "failed to close eventfd, errno=%d", errno); +#else + if (close(event->pipefd[1]) == -1) + usbi_warn(NULL, "failed to close pipe write end, errno=%d", errno); + if (close(event->pipefd[0]) == -1) + usbi_warn(NULL, "failed to close pipe read end, errno=%d", errno); +#endif +} + +void usbi_signal_event(usbi_event_t *event) +{ + uint64_t dummy = 1; + ssize_t r; + + r = write(EVENT_WRITE_FD(event), &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) + usbi_warn(NULL, "event write failed"); +} + +void usbi_clear_event(usbi_event_t *event) +{ + uint64_t dummy; + ssize_t r; + + r = read(EVENT_READ_FD(event), &dummy, sizeof(dummy)); + if (r != sizeof(dummy)) + usbi_warn(NULL, "event read failed"); +} + +#ifdef HAVE_TIMERFD +int usbi_create_timer(usbi_timer_t *timer) +{ + timer->timerfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC); + if (timer->timerfd == -1) { + usbi_warn(NULL, "failed to create timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +void usbi_destroy_timer(usbi_timer_t *timer) +{ + if (close(timer->timerfd) == -1) + usbi_warn(NULL, "failed to close timerfd, errno=%d", errno); +} + +int usbi_arm_timer(usbi_timer_t *timer, const struct timespec *timeout) +{ + const struct itimerspec it = { { 0, 0 }, { timeout->tv_sec, timeout->tv_nsec } }; + + if (timerfd_settime(timer->timerfd, TFD_TIMER_ABSTIME, &it, NULL) == -1) { + usbi_warn(NULL, "failed to arm timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} + +int usbi_disarm_timer(usbi_timer_t *timer) +{ + const struct itimerspec it = { { 0, 0 }, { 0, 0 } }; + + if (timerfd_settime(timer->timerfd, 0, &it, NULL) == -1) { + usbi_warn(NULL, "failed to disarm timerfd, errno=%d", errno); + return LIBUSB_ERROR_OTHER; + } + + return 0; +} +#endif + +int usbi_alloc_event_data(struct libusb_context *ctx) +{ + struct usbi_event_source *ievent_source; + struct pollfd *fds; + size_t i = 0; + + if (ctx->event_data) { + free(ctx->event_data); + ctx->event_data = NULL; + } + + ctx->event_data_cnt = 0; + for_each_event_source(ctx, ievent_source) + ctx->event_data_cnt++; + + fds = calloc(ctx->event_data_cnt, sizeof(*fds)); + if (!fds) + return LIBUSB_ERROR_NO_MEM; + + for_each_event_source(ctx, ievent_source) { + fds[i].fd = ievent_source->data.os_handle; + fds[i].events = ievent_source->data.poll_events; + i++; + } + + ctx->event_data = fds; + return 0; +} + +int usbi_wait_for_events(struct libusb_context *ctx, + struct usbi_reported_events *reported_events, int timeout_ms) +{ + struct pollfd *fds = ctx->event_data; + usbi_nfds_t nfds = (usbi_nfds_t)ctx->event_data_cnt; + int internal_fds, num_ready; + + usbi_dbg("poll() %u fds with timeout in %dms", (unsigned int)nfds, timeout_ms); + num_ready = poll(fds, nfds, timeout_ms); + usbi_dbg("poll() returned %d", num_ready); + if (num_ready == 0) { + if (usbi_using_timer(ctx)) + goto done; + return LIBUSB_ERROR_TIMEOUT; + } else if (num_ready == -1) { + if (errno == EINTR) + return LIBUSB_ERROR_INTERRUPTED; + usbi_err(ctx, "poll() failed, errno=%d", errno); + return LIBUSB_ERROR_IO; + } + + /* fds[0] is always the internal signalling event */ + if (fds[0].revents) { + reported_events->event_triggered = 1; + num_ready--; + } else { + reported_events->event_triggered = 0; + } + +#ifdef HAVE_OS_TIMER + /* on timer configurations, fds[1] is the timer */ + if (usbi_using_timer(ctx) && fds[1].revents) { + reported_events->timer_triggered = 1; + num_ready--; + } else { + reported_events->timer_triggered = 0; + } +#endif + + if (!num_ready) + goto done; + + /* the backend will never need to attempt to handle events on the + * library's internal file descriptors, so we determine how many are + * in use internally for this context and skip these when passing any + * remaining pollfds to the backend. */ + internal_fds = usbi_using_timer(ctx) ? 2 : 1; + fds += internal_fds; + nfds -= internal_fds; + + usbi_mutex_lock(&ctx->event_data_lock); + if (ctx->event_flags & USBI_EVENT_EVENT_SOURCES_MODIFIED) { + struct usbi_event_source *ievent_source; + + for_each_removed_event_source(ctx, ievent_source) { + usbi_nfds_t n; + + for (n = 0; n < nfds; n++) { + if (ievent_source->data.os_handle != fds[n].fd) + continue; + if (!fds[n].revents) + continue; + /* pollfd was removed between the creation of the fds array and + * here. remove triggered revent as it is no longer relevant. */ + usbi_dbg("fd %d was removed, ignoring raised events", fds[n].fd); + fds[n].revents = 0; + num_ready--; + break; + } + } + } + usbi_mutex_unlock(&ctx->event_data_lock); + + if (num_ready) { + assert(num_ready > 0); + reported_events->event_data = fds; + reported_events->event_data_count = (unsigned int)nfds; + } + +done: + reported_events->num_ready = num_ready; + return LIBUSB_SUCCESS; +} diff --git a/mac/libusb/os/events_posix.h b/mac/libusb/os/events_posix.h new file mode 100644 index 000000000..d81b5c4df --- /dev/null +++ b/mac/libusb/os/events_posix.h @@ -0,0 +1,59 @@ +/* + * libusb event abstraction on POSIX platforms + * + * Copyright © 2020 Chris Dickens + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LIBUSB_EVENTS_POSIX_H +#define LIBUSB_EVENTS_POSIX_H + +#include + +typedef int usbi_os_handle_t; +#define USBI_OS_HANDLE_FORMAT_STRING "fd %d" + +#ifdef HAVE_EVENTFD +typedef struct usbi_event { + int eventfd; +} usbi_event_t; +#define USBI_EVENT_OS_HANDLE(e) ((e)->eventfd) +#define USBI_EVENT_POLL_EVENTS POLLIN +#define USBI_INVALID_EVENT { -1 } +#else +typedef struct usbi_event { + int pipefd[2]; +} usbi_event_t; +#define USBI_EVENT_OS_HANDLE(e) ((e)->pipefd[0]) +#define USBI_EVENT_POLL_EVENTS POLLIN +#define USBI_INVALID_EVENT { { -1, -1 } } +#endif + +#ifdef HAVE_TIMERFD +#define HAVE_OS_TIMER 1 +typedef struct usbi_timer { + int timerfd; +} usbi_timer_t; +#define USBI_TIMER_OS_HANDLE(t) ((t)->timerfd) +#define USBI_TIMER_POLL_EVENTS POLLIN + +static inline int usbi_timer_valid(usbi_timer_t *timer) +{ + return timer->timerfd >= 0; +} +#endif + +#endif diff --git a/mac/libusb/os/poll_posix.c b/mac/libusb/os/poll_posix.c deleted file mode 100644 index 337714aa6..000000000 --- a/mac/libusb/os/poll_posix.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * poll_posix: poll compatibility wrapper for POSIX systems - * Copyright © 2013 RealVNC Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include - -#include -#include -#include -#include - -#include "libusbi.h" - -int usbi_pipe(int pipefd[2]) -{ -#if defined(HAVE_PIPE2) - int ret = pipe2(pipefd, O_CLOEXEC); -#else - int ret = pipe(pipefd); -#endif - - if (ret != 0) { - usbi_err(NULL, "failed to create pipe (%d)", errno); - return ret; - } - -#if !defined(HAVE_PIPE2) && defined(FD_CLOEXEC) - ret = fcntl(pipefd[0], F_GETFD); - if (ret == -1) { - usbi_err(NULL, "failed to get pipe fd flags (%d)", errno); - goto err_close_pipe; - } - ret = fcntl(pipefd[0], F_SETFD, ret | FD_CLOEXEC); - if (ret == -1) { - usbi_err(NULL, "failed to set pipe fd flags (%d)", errno); - goto err_close_pipe; - } - - ret = fcntl(pipefd[1], F_GETFD); - if (ret == -1) { - usbi_err(NULL, "failed to get pipe fd flags (%d)", errno); - goto err_close_pipe; - } - ret = fcntl(pipefd[1], F_SETFD, ret | FD_CLOEXEC); - if (ret == -1) { - usbi_err(NULL, "failed to set pipe fd flags (%d)", errno); - goto err_close_pipe; - } -#endif - - ret = fcntl(pipefd[1], F_GETFL); - if (ret == -1) { - usbi_err(NULL, "failed to get pipe fd status flags (%d)", errno); - goto err_close_pipe; - } - ret = fcntl(pipefd[1], F_SETFL, ret | O_NONBLOCK); - if (ret == -1) { - usbi_err(NULL, "failed to set pipe fd status flags (%d)", errno); - goto err_close_pipe; - } - - return 0; - -err_close_pipe: - close(pipefd[0]); - close(pipefd[1]); - return ret; -} diff --git a/mac/libusb/os/poll_posix.h b/mac/libusb/os/poll_posix.h deleted file mode 100644 index 5b4b2c905..000000000 --- a/mac/libusb/os/poll_posix.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef LIBUSB_POLL_POSIX_H -#define LIBUSB_POLL_POSIX_H - -#define usbi_write write -#define usbi_read read -#define usbi_close close -#define usbi_poll poll - -int usbi_pipe(int pipefd[2]); - -#endif /* LIBUSB_POLL_POSIX_H */ diff --git a/mac/libusb/os/threads_posix.c b/mac/libusb/os/threads_posix.c index 16a7578b8..0e0e22134 100644 --- a/mac/libusb/os/threads_posix.c +++ b/mac/libusb/os/threads_posix.c @@ -19,23 +19,39 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "libusbi.h" -#include -#if defined(__linux__) || defined(__OpenBSD__) -# if defined(__OpenBSD__) -# define _BSD_SOURCE -# endif +#include +#if defined(__ANDROID__) # include +#elif defined(__HAIKU__) +# include +#elif defined(__linux__) # include -#elif defined(__APPLE__) -# include -#elif defined(__CYGWIN__) -# include +# include +#elif defined(__NetBSD__) +# include +#elif defined(__OpenBSD__) +# define _BSD_SOURCE +# include +# include +#elif defined(__sun__) +# include #endif -#include "threads_posix.h" -#include "libusbi.h" +void usbi_cond_init(pthread_cond_t *cond) +{ +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + pthread_condattr_t condattr; + + PTHREAD_CHECK(pthread_condattr_init(&condattr)); + PTHREAD_CHECK(pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC)); + PTHREAD_CHECK(pthread_cond_init(cond, &condattr)); + PTHREAD_CHECK(pthread_condattr_destroy(&condattr)); +#else + PTHREAD_CHECK(pthread_cond_init(cond, NULL)); +#endif +} int usbi_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timeval *tv) @@ -43,38 +59,71 @@ int usbi_cond_timedwait(pthread_cond_t *cond, struct timespec timeout; int r; - r = usbi_backend.clock_gettime(USBI_CLOCK_REALTIME, &timeout); - if (r < 0) - return r; +#ifdef HAVE_PTHREAD_CONDATTR_SETCLOCK + usbi_get_monotonic_time(&timeout); +#else + usbi_get_real_time(&timeout); +#endif timeout.tv_sec += tv->tv_sec; - timeout.tv_nsec += tv->tv_usec * 1000; - while (timeout.tv_nsec >= 1000000000L) { - timeout.tv_nsec -= 1000000000L; + timeout.tv_nsec += tv->tv_usec * 1000L; + if (timeout.tv_nsec >= NSEC_PER_SEC) { + timeout.tv_nsec -= NSEC_PER_SEC; timeout.tv_sec++; } - return pthread_cond_timedwait(cond, mutex, &timeout); + r = pthread_cond_timedwait(cond, mutex, &timeout); + if (r == 0) + return 0; + else if (r == ETIMEDOUT) + return LIBUSB_ERROR_TIMEOUT; + else + return LIBUSB_ERROR_OTHER; } -int usbi_get_tid(void) +unsigned int usbi_get_tid(void) { - int ret; + static _Thread_local unsigned int tl_tid; + int tid; + + if (tl_tid) + return tl_tid; + #if defined(__ANDROID__) - ret = gettid(); + tid = gettid(); +#elif defined(__APPLE__) +#ifdef HAVE_PTHREAD_THREADID_NP + uint64_t thread_id; + + if (pthread_threadid_np(NULL, &thread_id) == 0) + tid = (int)thread_id; + else + tid = -1; +#else + tid = (int)pthread_mach_thread_np(pthread_self()); +#endif +#elif defined(__HAIKU__) + tid = get_pthread_thread_id(pthread_self()); #elif defined(__linux__) - ret = syscall(SYS_gettid); + tid = (int)syscall(SYS_gettid); +#elif defined(__NetBSD__) + tid = _lwp_self(); #elif defined(__OpenBSD__) /* The following only works with OpenBSD > 5.1 as it requires - real thread support. For 5.1 and earlier, -1 is returned. */ - ret = syscall(SYS_getthrid); -#elif defined(__APPLE__) - ret = (int)pthread_mach_thread_np(pthread_self()); -#elif defined(__CYGWIN__) - ret = GetCurrentThreadId(); + * real thread support. For 5.1 and earlier, -1 is returned. */ + tid = syscall(SYS_getthrid); +#elif defined(__sun__) + tid = _lwp_self(); #else - ret = -1; + tid = -1; #endif -/* TODO: NetBSD thread ID support */ - return ret; + + if (tid == -1) { + /* If we don't have a thread ID, at least return a unique + * value that can be used to distinguish individual + * threads. */ + tid = (int)(intptr_t)pthread_self(); + } + + return tl_tid = (unsigned int)tid; } diff --git a/mac/libusb/os/threads_posix.h b/mac/libusb/os/threads_posix.h index 9f1ef94bc..932283402 100644 --- a/mac/libusb/os/threads_posix.h +++ b/mac/libusb/os/threads_posix.h @@ -22,67 +22,63 @@ #define LIBUSB_THREADS_POSIX_H #include -#ifdef HAVE_SYS_TIME_H -#include -#endif + +#define PTHREAD_CHECK(expression) ASSERT_EQ(expression, 0) #define USBI_MUTEX_INITIALIZER PTHREAD_MUTEX_INITIALIZER typedef pthread_mutex_t usbi_mutex_static_t; static inline void usbi_mutex_static_lock(usbi_mutex_static_t *mutex) { - (void)pthread_mutex_lock(mutex); + PTHREAD_CHECK(pthread_mutex_lock(mutex)); } static inline void usbi_mutex_static_unlock(usbi_mutex_static_t *mutex) { - (void)pthread_mutex_unlock(mutex); + PTHREAD_CHECK(pthread_mutex_unlock(mutex)); } typedef pthread_mutex_t usbi_mutex_t; -static inline int usbi_mutex_init(usbi_mutex_t *mutex) +static inline void usbi_mutex_init(usbi_mutex_t *mutex) { - return pthread_mutex_init(mutex, NULL); + PTHREAD_CHECK(pthread_mutex_init(mutex, NULL)); } static inline void usbi_mutex_lock(usbi_mutex_t *mutex) { - (void)pthread_mutex_lock(mutex); + PTHREAD_CHECK(pthread_mutex_lock(mutex)); } static inline void usbi_mutex_unlock(usbi_mutex_t *mutex) { - (void)pthread_mutex_unlock(mutex); + PTHREAD_CHECK(pthread_mutex_unlock(mutex)); } static inline int usbi_mutex_trylock(usbi_mutex_t *mutex) { - return pthread_mutex_trylock(mutex); + return pthread_mutex_trylock(mutex) == 0; } static inline void usbi_mutex_destroy(usbi_mutex_t *mutex) { - (void)pthread_mutex_destroy(mutex); + PTHREAD_CHECK(pthread_mutex_destroy(mutex)); } typedef pthread_cond_t usbi_cond_t; -static inline void usbi_cond_init(pthread_cond_t *cond) -{ - (void)pthread_cond_init(cond, NULL); -} -static inline int usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) +void usbi_cond_init(pthread_cond_t *cond); +static inline void usbi_cond_wait(usbi_cond_t *cond, usbi_mutex_t *mutex) { - return pthread_cond_wait(cond, mutex); + PTHREAD_CHECK(pthread_cond_wait(cond, mutex)); } int usbi_cond_timedwait(usbi_cond_t *cond, usbi_mutex_t *mutex, const struct timeval *tv); static inline void usbi_cond_broadcast(usbi_cond_t *cond) { - (void)pthread_cond_broadcast(cond); + PTHREAD_CHECK(pthread_cond_broadcast(cond)); } static inline void usbi_cond_destroy(usbi_cond_t *cond) { - (void)pthread_cond_destroy(cond); + PTHREAD_CHECK(pthread_cond_destroy(cond)); } typedef pthread_key_t usbi_tls_key_t; static inline void usbi_tls_key_create(usbi_tls_key_t *key) { - (void)pthread_key_create(key, NULL); + PTHREAD_CHECK(pthread_key_create(key, NULL)); } static inline void *usbi_tls_key_get(usbi_tls_key_t key) { @@ -90,13 +86,13 @@ static inline void *usbi_tls_key_get(usbi_tls_key_t key) } static inline void usbi_tls_key_set(usbi_tls_key_t key, void *ptr) { - (void)pthread_setspecific(key, ptr); + PTHREAD_CHECK(pthread_setspecific(key, ptr)); } static inline void usbi_tls_key_delete(usbi_tls_key_t key) { - (void)pthread_key_delete(key); + PTHREAD_CHECK(pthread_key_delete(key)); } -int usbi_get_tid(void); +unsigned int usbi_get_tid(void); #endif /* LIBUSB_THREADS_POSIX_H */ diff --git a/mac/libusb/strerror.c b/mac/libusb/strerror.c index d2be0e2a0..9445fa9e8 100644 --- a/mac/libusb/strerror.c +++ b/mac/libusb/strerror.c @@ -17,28 +17,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - -#include -#include -#include -#if defined(HAVE_STRINGS_H) -#include -#endif - #include "libusbi.h" -#if defined(_MSC_VER) -#define strncasecmp _strnicmp -#endif - -static size_t usbi_locale = 0; +#include +#include /** \ingroup libusb_misc * How to add a new \ref libusb_strerror() translation: *
    *
  1. Download the latest \c strerror.c from:
    - * https://raw.github.com/libusb/libusb/master/libusb/sterror.c
  2. + * https://raw.github.com/libusb/libusb/master/libusb/strerror.c *
  3. Open the file in an UTF-8 capable editor
  4. *
  5. Add the 2 letter ISO 639-1 * code for your locale at the end of \c usbi_locale_supported[]
    @@ -53,15 +41,15 @@ static size_t usbi_locale = 0; * "Success", * ... * "Other error", - * } + * }, * };\endcode
  6. *
  7. Translate each of the English messages from the section you copied into your language
  8. *
  9. Save the file (in UTF-8 format) and send it to \c libusb-devel\@lists.sourceforge.net
  10. *
*/ -static const char* usbi_locale_supported[] = { "en", "nl", "fr", "ru" }; -static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { +static const char * const usbi_locale_supported[] = { "en", "nl", "fr", "ru", "de", "hu" }; +static const char * const usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUSB_ERROR_COUNT] = { { /* English (en) */ "Success", "Input/Output Error", @@ -122,9 +110,41 @@ static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUS "Память исчерпана", "Операция не поддерживается данной платформой", "Неизвестная ошибка" - } + }, { /* German (de) */ + "Erfolgreich", + "Eingabe-/Ausgabefehler", + "Ungültiger Parameter", + "Keine Berechtigung (Zugriffsrechte fehlen)", + "Kein passendes Gerät gefunden (es könnte entfernt worden sein)", + "Entität nicht gefunden", + "Die Ressource ist belegt", + "Die Wartezeit für die Operation ist abgelaufen", + "Mehr Daten empfangen als erwartet", + "Datenübergabe unterbrochen (broken pipe)", + "Unterbrechung während des Betriebssystemaufrufs", + "Nicht genügend Hauptspeicher verfügbar", + "Die Operation wird nicht unterstützt oder ist auf dieser Platform nicht implementiert", + "Allgemeiner Fehler", + }, { /* Hungarian (hu) */ + "Sikeres", + "Be-/kimeneti hiba", + "Érvénytelen paraméter", + "Hozzáférés megtagadva", + "Az eszköz nem található (eltávolították?)", + "Nem található", + "Az erőforrás foglalt", + "Időtúllépés", + "Túlcsordulás", + "Törött adatcsatorna", + "Rendszerhívás megszakítva", + "Nincs elég memória", + "A művelet nem támogatott ezen a rendszeren", + "Általános hiba", + }, }; +static const char * const (*usbi_error_strings)[LIBUSB_ERROR_COUNT] = &usbi_localized_errors[0]; + /** \ingroup libusb_misc * Set the language, and only the language, not the encoding! used for * translatable libusb messages. @@ -134,7 +154,7 @@ static const char* usbi_localized_errors[ARRAYSIZE(usbi_locale_supported)][LIBUS * used, and only 2 letter ISO 639-1 codes are accepted for it, such as "de". * The optional region, country_region or codeset parts are ignored. This * means that functions which return translatable strings will NOT honor the - * specified encoding. + * specified encoding. * All strings returned are encoded as UTF-8 strings. * * If libusb_setlocale() is not called, all messages will be in English. @@ -159,19 +179,20 @@ int API_EXPORTED libusb_setlocale(const char *locale) { size_t i; - if ( (locale == NULL) || (strlen(locale) < 2) - || ((strlen(locale) > 2) && (locale[2] != '-') && (locale[2] != '_') && (locale[2] != '.')) ) + if (!locale || strlen(locale) < 2 + || (locale[2] != '\0' && locale[2] != '-' && locale[2] != '_' && locale[2] != '.')) return LIBUSB_ERROR_INVALID_PARAM; - for (i=0; i= ARRAYSIZE(usbi_locale_supported)) { + + if (i == ARRAYSIZE(usbi_locale_supported)) return LIBUSB_ERROR_NOT_FOUND; - } - usbi_locale = i; + usbi_error_strings = &usbi_localized_errors[i]; return LIBUSB_SUCCESS; } @@ -189,14 +210,14 @@ int API_EXPORTED libusb_setlocale(const char *locale) * \param errcode the error code whose description is desired * \returns a short description of the error code in UTF-8 encoding */ -DEFAULT_VISIBILITY const char* LIBUSB_CALL libusb_strerror(enum libusb_error errcode) +DEFAULT_VISIBILITY const char * LIBUSB_CALL libusb_strerror(int errcode) { int errcode_index = -errcode; - if ((errcode_index < 0) || (errcode_index >= LIBUSB_ERROR_COUNT)) { + if (errcode_index < 0 || errcode_index >= LIBUSB_ERROR_COUNT) { /* "Other Error", which should always be our last message, is returned */ errcode_index = LIBUSB_ERROR_COUNT - 1; } - return usbi_localized_errors[usbi_locale][errcode_index]; + return (*usbi_error_strings)[errcode_index]; } diff --git a/mac/libusb/sync.c b/mac/libusb/sync.c index a609f65f4..adc95b402 100644 --- a/mac/libusb/sync.c +++ b/mac/libusb/sync.c @@ -1,6 +1,9 @@ +/* -*- Mode: C; indent-tabs-mode:t ; c-basic-offset:8 -*- */ /* * Synchronous I/O functions for libusb * Copyright © 2007-2008 Daniel Drake + * Copyright © 2019 Nathan Hjelm + * Copyright © 2019 Google LLC. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -17,15 +20,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include +#include "libusbi.h" -#include -#include -#include #include -#include "libusbi.h" - /** * @defgroup libusb_syncio Synchronous device I/O * @@ -57,6 +55,11 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) libusb_cancel_transfer(transfer); continue; } + if (NULL == transfer->dev_handle) { + /* transfer completion after libusb_close() */ + transfer->status = LIBUSB_TRANSFER_NO_DEVICE; + *completed = 1; + } } } @@ -78,7 +81,7 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) * (depending on direction bits within bmRequestType) * \param wLength the length field for the setup packet. The data buffer should * be at least this size. - * \param timeout timeout (in millseconds) that this function should wait + * \param timeout timeout (in milliseconds) that this function should wait * before giving up due to no response being received. For an unlimited * timeout, use value 0. * \returns on success, the number of bytes actually transferred @@ -88,7 +91,7 @@ static void sync_transfer_wait_for_completion(struct libusb_transfer *transfer) * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns LIBUSB_ERROR_BUSY if called from event handling context * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than - * the operating system and/or hardware can support + * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, @@ -107,7 +110,7 @@ int API_EXPORTED libusb_control_transfer(libusb_device_handle *dev_handle, if (!transfer) return LIBUSB_ERROR_NO_MEM; - buffer = (unsigned char*) malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); + buffer = malloc(LIBUSB_CONTROL_SETUP_SIZE + wLength); if (!buffer) { libusb_free_transfer(transfer); return LIBUSB_ERROR_NO_MEM; @@ -240,7 +243,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * underlying O/S requirements, meaning that the timeout may expire after * the first few chunks have completed. libusb is careful not to lose any data * that may have been transferred; do not assume that timeout conditions - * indicate a complete lack of I/O. + * indicate a complete lack of I/O. See \ref asynctimeout for more details. * * \param dev_handle a handle for the device to communicate with * \param endpoint the address of a valid endpoint to communicate with @@ -252,7 +255,7 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), * it is legal to pass a NULL pointer if you do not wish to receive this * information. - * \param timeout timeout (in millseconds) that this function should wait + * \param timeout timeout (in milliseconds) that this function should wait * before giving up due to no response being received. For an unlimited * timeout, use value 0. * @@ -264,11 +267,13 @@ static int do_sync_bulk_transfer(struct libusb_device_handle *dev_handle, * \ref libusb_packetoverflow * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns LIBUSB_ERROR_BUSY if called from event handling context + * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other failures */ -int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, - unsigned char endpoint, unsigned char *data, int length, int *transferred, - unsigned int timeout) +int API_EXPORTED libusb_bulk_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) { return do_sync_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_BULK); @@ -291,7 +296,7 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, * underlying O/S requirements, meaning that the timeout may expire after * the first few chunks have completed. libusb is careful not to lose any data * that may have been transferred; do not assume that timeout conditions - * indicate a complete lack of I/O. + * indicate a complete lack of I/O. See \ref asynctimeout for more details. * * The default endpoint bInterval value is used as the polling interval. * @@ -305,7 +310,7 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, * transferred. Since version 1.0.21 (\ref LIBUSB_API_VERSION >= 0x01000105), * it is legal to pass a NULL pointer if you do not wish to receive this * information. - * \param timeout timeout (in millseconds) that this function should wait + * \param timeout timeout (in milliseconds) that this function should wait * before giving up due to no response being received. For an unlimited * timeout, use value 0. * @@ -316,11 +321,13 @@ int API_EXPORTED libusb_bulk_transfer(struct libusb_device_handle *dev_handle, * \ref libusb_packetoverflow * \returns LIBUSB_ERROR_NO_DEVICE if the device has been disconnected * \returns LIBUSB_ERROR_BUSY if called from event handling context + * \returns LIBUSB_ERROR_INVALID_PARAM if the transfer size is larger than + * the operating system and/or hardware can support (see \ref asynclimits) * \returns another LIBUSB_ERROR code on other error */ -int API_EXPORTED libusb_interrupt_transfer( - struct libusb_device_handle *dev_handle, unsigned char endpoint, - unsigned char *data, int length, int *transferred, unsigned int timeout) +int API_EXPORTED libusb_interrupt_transfer(libusb_device_handle *dev_handle, + unsigned char endpoint, unsigned char *data, int length, + int *transferred, unsigned int timeout) { return do_sync_bulk_transfer(dev_handle, endpoint, data, length, transferred, timeout, LIBUSB_TRANSFER_TYPE_INTERRUPT); diff --git a/mac/libusb/version.h b/mac/libusb/version.h index c6dfe3709..d8ebde4e3 100644 --- a/mac/libusb/version.h +++ b/mac/libusb/version.h @@ -7,7 +7,7 @@ #define LIBUSB_MINOR 0 #endif #ifndef LIBUSB_MICRO -#define LIBUSB_MICRO 22 +#define LIBUSB_MICRO 24 #endif #ifndef LIBUSB_NANO #define LIBUSB_NANO 0 diff --git a/mac/libusb/version_nano.h b/mac/libusb/version_nano.h index 90a782a6b..0f100a824 100644 --- a/mac/libusb/version_nano.h +++ b/mac/libusb/version_nano.h @@ -1 +1 @@ -#define LIBUSB_NANO 11312 +#define LIBUSB_NANO 11584 -- 2.30.2